Я хочу сделать диаграмму, подобную самсунгу, прокрутке поддержки и центрированию выбранного элемента. Я использую эту библиотеку https://github.com/PhilJay/MPAndroidChart. Но работает только в тапе, я понятия не имею, как настроить его для прокрутки.
Я хочу что-то вроде этого (посмотрите на черный кружок):
Диаграмма здоровья Samsung
URL-адрес видео: https://www.youtube.com/watch?v=_rcJraCzdNw
Изображение:
Диаграмма Samsung, по центру выделенный элемент при нажатии или прокрутке
Моя диаграмма
Но моя диаграмма выглядит так.
URL-адрес видео: https://www.youtube.com/watch?v=ZQGj4CcBygU
Изображение:
Моя диаграмма, центрированный выбранный элемент только при нажатии, а не при прокрутке.
Я попробовал onChartGestureEnd () внутри setOnGestureListener (), но позиция x - это то место, где мой палец впервые касается экрана. Пример: допустим, я проведу пальцем вправо (слева направо), первое касание моего пальца - 18, и центральная позиция будет позицией 14. Я хочу получить позицию 14 позиции, но не знаю, как это сделать. Я думал, что замедление, поэтому я вызываю stopDeceleration (), но не работает.
Я тоже пытался с ViewPager
, но тоже не получилось: (
@Override
public void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) {
MPPointD pointD = MPPointD.getInstance(0,0);
Transformer transformer = mChartOne.getTransformer(AxisDependency.LEFT);
transformer.getValuesByTouchPoint(me.getX(), me.getY(), pointD);
int roundX = round((float) pointD.x);
int roundY = round((float) pointD.y);
mChartOne.centerViewToAnimated(roundX, roundY, AxisDependency.LEFT, 200);
Utils.selectedIndex = roundX;
showSelectedInfo(roundX);
}
Это мой код для этого действия (пожалуйста, игнорируйте переменную * Two):
BarChartActivity.java
public class BarChartActivity extends AppCompatActivity {
private BarChart mChartOne;
private BarChart mChartTwo;
private Spinner mSpOption;
private TextView mTvDate;
private TextView mTvPower;
private TextView mTvCost;
private Legend lOne;
private Legend lTwo;
private LimitLine llOne;
private LimitLine llTwo;
private XAxis xAxisOne;
private XAxis xAxisTwo;
private YAxis leftAxisOne;
private YAxis leftAxisTwo;
private YAxis rightAxisOne;
private YAxis rightAxisTwo;
private ArrayList<BarEntry> barValuesOne = new ArrayList<>();
private ArrayList<BarEntry> barValuesTwo = new ArrayList<>();
private List<String> axisLabelOne = new ArrayList<>();
private List<String> axisLabelTwo = new ArrayList<>();
private List<Statistic> statisticList = new ArrayList<>();
private List<String> option = new ArrayList<>();
private String TAG = "bebe";
private String[] dummyCost;
private String[] dummyPower;
private String[] dummyDate;
private int lastIndex;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_be_chart);
setTitle("BarChartActivity");
//todo : create another layout for different dpi
mChartOne = findViewById(R.id.chart1);
mChartTwo = findViewById(R.id.chart2);
mSpOption = findViewById(R.id.sp_option);
mTvDate = findViewById(R.id.tv_date);
mTvPower = findViewById(R.id.tv_power);
mTvCost = findViewById(R.id.tv_cost);
option.add("Days");
option.add("Week");
option.add("Month");
dummyCost = getResources().getStringArray(R.array.dummy_cost);
dummyDate = getResources().getStringArray(R.array.dummy_date);
dummyPower = getResources().getStringArray(R.array.dummy_power);
int lastNum = 0;
for (int i = 0; i < dummyPower.length; i++) {
Statistic stat = new Statistic();
stat.cost = dummyCost[i];
stat.date = dummyDate[i];
stat.power = dummyPower[i];
statisticList.add(stat);
String numberStr = stat.date.substring(8,10);
int numStr = Integer.parseInt(numberStr); // remove 0, 01 -> 1
axisLabelOne.add(String.valueOf(numStr));
lastNum = numStr;
}
// empty label
for (int i = 0; i < 3; i++) {
lastNum++;
if (lastNum > 31) lastNum = 1;
axisLabelOne.add(String.valueOf(lastNum));
}
axisLabelTwo = new ArrayList<>(Arrays.asList(getResources().getStringArray(R.array.label_hour)));
ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, option);
dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mSpOption.setAdapter(dataAdapter);
mSpOption.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
barValuesOne.clear();
mChartOne.animateY(500);
setDataOne();
mChartOne.setVisibleXRangeMaximum(7);
BarEntry lastEntry = barValuesOne.get(lastIndex);
Utils.selectedIndex = lastEntry.getX();
showSelectedInfo(lastIndex);
mChartOne.centerViewToAnimated(lastEntry.getX(), lastEntry.getY(),
AxisDependency.LEFT, 0);
mChartOne.invalidate();
barValuesTwo.clear();
mChartTwo.animateY(500);
setDataTwo();
mChartTwo.invalidate();
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
loadChartOne();
loadChartTwo();
}
private void loadChartOne(){
mChartOne.setDrawBarShadow(false);
mChartOne.setDrawValueAboveBar(true);
mChartOne.getDescription().setEnabled(false);
mChartOne.setPinchZoom(false);
mChartOne.setDrawGridBackground(false);
mChartOne.setDragEnabled(true);
mChartOne.setScaleEnabled(false);
mChartOne.setPinchZoom(false);
mChartOne.setDoubleTapToZoomEnabled(false);
mChartOne.setDrawBorders(false);
mChartOne.setExtraOffsets(0f,
getResources().getDimensionPixelSize(R.dimen.padding_label),
0f,
getResources().getDimensionPixelSize(R.dimen.padding_label));
mChartOne.setOnChartValueSelectedListener(new OnChartValueSelectedListener() {
@Override
public void onValueSelected(Entry e, Highlight h) {
if (e == null)
return;
Log.e(TAG, "onValueSelected: "+e);
Log.e(TAG, "data: "+ axisLabelOne.get((int) e.getX()));
mChartOne.centerViewToAnimated(e.getX(), e.getY(),
mChartOne.getData().getDataSetByIndex(h.getDataSetIndex())
.getAxisDependency(), 200);
Utils.selectedIndex = e.getX();
showSelectedInfo((int) e.getX());
}
@Override
public void onNothingSelected() {
}
});
mChartOne.setOnChartGestureListener(new OnChartGestureListener() {
@Override
public void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) {
}
@Override
public void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) {
MPPointD pointD = MPPointD.getInstance(0,0);
Transformer transformer = mChartOne.getTransformer(AxisDependency.LEFT);
transformer.getValuesByTouchPoint(me.getX(), me.getY(), pointD);
int roundX = round((float) pointD.x);
int roundY = round((float) pointD.y);
mChartOne.centerViewToAnimated(roundX, roundY, AxisDependency.LEFT, 200);
Utils.selectedIndex = roundX;
showSelectedInfo(roundX);
}
@Override
public void onChartLongPressed(MotionEvent me) {
}
@Override
public void onChartDoubleTapped(MotionEvent me) {
}
@Override
public void onChartSingleTapped(MotionEvent me) {
}
@Override
public void onChartFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY) {
}
@Override
public void onChartScale(MotionEvent me, float scaleX, float scaleY) {
}
@Override
public void onChartTranslate(MotionEvent me, float dX, float dY) {
}
});
ValueFormatter xAxisFormatter = new IndexAxisValueFormatter(axisLabelOne);
xAxisOne = mChartOne.getXAxis();
xAxisOne.setPosition(XAxisPosition.BOTTOM);
xAxisOne.setTextSize(16f);
xAxisOne.setGranularity(1f); // only intervals of 1 day
xAxisOne.setLabelCount(7);
xAxisOne.setValueFormatter(xAxisFormatter);
xAxisOne.setDrawGridLines(false);
xAxisOne.setDrawAxisLine(false);
xAxisOne.customLabel = true;
xAxisOne.setYOffset(getResources().getDimensionPixelSize(R.dimen.padding_label));
ValueFormatter custom = new MyValueFormatter(" W");
leftAxisOne = mChartOne.getAxisLeft();
leftAxisOne.setLabelCount(8, false);
leftAxisOne.setValueFormatter(custom);
leftAxisOne.setPosition(YAxisLabelPosition.OUTSIDE_CHART);
leftAxisOne.setSpaceTop(15f);
leftAxisOne.setAxisMinimum(1f); // this replaces setStartAtZero(true)
leftAxisOne.setEnabled(false);
llOne = new LimitLine(16f, "Usage warning (16 W)");
llOne.setLineColor(Color.RED);
llOne.setLineWidth(1f);
llOne.setTextColor(Color.BLACK);
llOne.setTextSize(12f);
llOne.enableDashedLine(4, 3, 0);
rightAxisOne = mChartOne.getAxisRight();
rightAxisOne.setDrawGridLines(true);
rightAxisOne.setLabelCount(2, false);
rightAxisOne.setValueFormatter(custom);
rightAxisOne.setSpaceTop(15f);
rightAxisOne.setAxisMinimum(1f); // this replaces setStartAtZero(true)
rightAxisOne.setCenterAxisLabels(true);
rightAxisOne.setPosition(YAxisLabelPosition.INSIDE_CHART);
rightAxisOne.enableGridDashedLine(4, 3, 0);
rightAxisOne.setGridColor(ContextCompat.getColor(this, R.color.bar_color));
rightAxisOne.addLimitLine(llOne);
rightAxisOne.setDrawLimitLinesBehindData(false);
rightAxisOne.setDrawZeroLine(false);
rightAxisOne.setDrawAxisLine(false);
lOne = mChartOne.getLegend();
lOne.setEnabled(false);
}
private void loadChartTwo(){
mChartTwo.setDrawBarShadow(false);
mChartTwo.setDrawValueAboveBar(false);
mChartTwo.getDescription().setEnabled(false);
mChartTwo.setPinchZoom(false);
mChartTwo.setDrawGridBackground(false);
mChartTwo.setDragEnabled(false);
mChartTwo.setScaleEnabled(false);
mChartTwo.setPinchZoom(false);
mChartTwo.setDoubleTapToZoomEnabled(false);
mChartTwo.setDrawBorders(false);
mChartTwo.setExtraOffsets(0f,
getResources().getDimensionPixelSize(R.dimen.padding_label),
0f,
getResources().getDimensionPixelSize(R.dimen.padding_label));
MyMarkerView mv = new MyMarkerView(this, R.layout.custom_marker_view);
mv.setChartView(mChartTwo); // For bounds control
mChartTwo.setMarker(mv); // Set the marker to the chart
ValueFormatter xAxisFormatter = new IndexAxisValueFormatter(axisLabelTwo);
xAxisTwo = mChartTwo.getXAxis();
xAxisTwo.setPosition(XAxisPosition.BOTTOM);
xAxisTwo.setTextSize(10f);
xAxisTwo.setValueFormatter(xAxisFormatter);
xAxisTwo.setDrawGridLines(false);
xAxisTwo.setDrawAxisLine(true);
xAxisTwo.setLabelCount(5);
xAxisTwo.customLabel = false;
leftAxisTwo = mChartTwo.getAxisLeft();
leftAxisTwo.setAxisMinimum(1f);
leftAxisTwo.setEnabled(false);
rightAxisTwo = mChartTwo.getAxisRight();
rightAxisTwo.setAxisMinimum(1f);
rightAxisTwo.setEnabled(false);
lTwo = mChartTwo.getLegend();
lTwo.setEnabled(false);
}
private void setDataOne() {
// start empty space
Utils.unselectedIndex[0] = 0f;
Utils.unselectedIndex[1] = 1f;
Utils.unselectedIndex[2] = 2f;
Utils.firstSelectedIndex = 3f;
barValuesOne.add(new BarEntry(Utils.unselectedIndex[0], 0));
barValuesOne.add(new BarEntry(Utils.unselectedIndex[1], 0));
barValuesOne.add(new BarEntry(Utils.unselectedIndex[2], 0));
float start = Utils.firstSelectedIndex;
float tmpStart = start;
for (int i = (int) start; i < dummyPower.length; i++) {
tmpStart = i;
lastIndex = i;
float val = Float.parseFloat(dummyPower[i]);
barValuesOne.add(new BarEntry(i, val));
Utils.lastSelectedIndex = i;
}
// end empty space
barValuesOne.add(new BarEntry(++tmpStart, 0));
Utils.unselectedIndex[3] = tmpStart;
barValuesOne.add(new BarEntry(++tmpStart, 0));
Utils.unselectedIndex[4] = tmpStart;
barValuesOne.add(new BarEntry(++tmpStart, 0));
Utils.unselectedIndex[5] = tmpStart;
BarDataSet set1;
if (mChartOne.getData() != null &&
mChartOne.getData().getDataSetCount() > 0) {
set1 = (BarDataSet) mChartOne.getData().getDataSetByIndex(0);
set1.setValues(barValuesOne);
mChartOne.getData().notifyDataChanged();
mChartOne.notifyDataSetChanged();
} else {
set1 = new BarDataSet(barValuesOne, "The year 2017");
set1.setDrawIcons(false);
set1.setColor(ContextCompat.getColor(this, R.color.bar_color));
set1.setHighLightColor(ContextCompat.getColor(this, R.color.bar_color));
ArrayList<IBarDataSet> dataSets = new ArrayList<>();
dataSets.add(set1);
BarData data = new BarData(dataSets);
data.setValueTextSize(8f);
data.setBarWidth(0.2f);
mChartOne.setData(data);
}
}
private void setDataTwo() {
float start = 0f;
int count = 24;
int range = 20;
Calendar cal = Calendar.getInstance();
int hour = cal.get(Calendar.HOUR_OF_DAY);
for (int i = (int) start; i < start + count; i++) {
float val = (float) (Math.random() * (range + 1));
if (val < 4) val += 6.5;
if (i < hour) barValuesTwo.add(new BarEntry(i, val));
else barValuesTwo.add(new BarEntry(i, 0));
}
BarDataSet set1;
if (mChartTwo.getData() != null &&
mChartTwo.getData().getDataSetCount() > 0) {
set1 = (BarDataSet) mChartTwo.getData().getDataSetByIndex(0);
set1.setValues(barValuesTwo);
set1.setDrawValues(false);
mChartTwo.getData().notifyDataChanged();
mChartTwo.notifyDataSetChanged();
} else {
set1 = new BarDataSet(barValuesTwo, "The year 2017");
set1.setDrawIcons(false);
set1.setDrawValues(false);
set1.setColor(ContextCompat.getColor(this, R.color.bar_hour_color));
set1.setHighLightColor(ContextCompat.getColor(this, R.color.bar_hour_color));
ArrayList<IBarDataSet> dataSets = new ArrayList<>();
dataSets.add(set1);
BarData data = new BarData(dataSets);
data.setValueTextSize(12f);
data.setBarWidth(0.2f);
mChartTwo.setData(data);
}
}
private final RectF onValueSelectedRectF = new RectF();
private void showSelectedInfo(int position){
boolean unselected = false;
for (int i = 0; i < Utils.unselectedIndex.length; i++) {
if (position == Utils.unselectedIndex[i]) {
unselected = true;
break;
}
}
if (!unselected && position == Utils.selectedIndex) {
Statistic stat = statisticList.get(position);
setTextData(stat);
} else if (unselected){
if (position > Utils.lastSelectedIndex ||
position < Utils.firstSelectedIndex){
if ((position <= Utils.firstSelectedIndex)){
Statistic stat = statisticList.get((int) Utils.firstSelectedIndex);
setTextData(stat);
} else if ((position >= Utils.lastSelectedIndex)) {
Statistic stat = statisticList.get((int) Utils.lastSelectedIndex);
setTextData(stat);
}
}
}
}
private void setTextData(Statistic stat){
int cost = Integer.parseInt(stat.cost);
String thousandCost = NumberFormat.getNumberInstance(Locale.GERMAN).format(cost);
mTvDate.setText(getDateString(stat.date));
mTvPower.setText(stat.power + " W");
mTvCost.setText("(Rp "+thousandCost+")");
}
private String getDateString(String rawDate) {
String date = rawDate.replace("-","").substring(0, 8);
String year = date.substring(0,4);
String month = date.substring(4,6);
String day = date.substring(6,8);
String monthName = getMonthName(month);
return String.valueOf(new StringBuilder().append(day).append(" ").append(monthName)
.append(" ").append(year));
}
private String getMonthName(String data) {
int month = Integer.parseInt(data);
String[] monthName = {"Januari", "Februari", "Maret", "April", "Mei", "Juni", "Juli",
"Agustus", "September", "Oktober", "November", "Desember"};
return monthName[month-1];
}
public int round(float f) {
int c = (int) ((f) + 0.5f);
float n = f + 0.5f;
return (n - c) % 2 == 0 ? (int) f : c;
}
}
Это мой макет:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_below="@id/chart1"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="#e1e1e1"
android:layout_marginTop="-48dp"
android:layout_centerHorizontal="true">
<View
android:id="@+id/v_selected_label"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_centerInParent="true"
android:background="@drawable/selected_bar_label"/>
<View
android:id="@+id/v_tooltip"
android:layout_width="8dp"
android:layout_height="8dp"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
android:background="@drawable/tooltip"/>
</RelativeLayout>
<com.github.mikephil.charting.charts.BarChart
android:id="@+id/chart1"
android:layout_width="match_parent"
android:layout_height="240dp"
android:padding="0dp"
android:layout_marginTop="16dp"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:layout_marginBottom="0dp"/>
<Spinner
android:id="@+id/sp_option"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:background="@android:color/white"/>
</RelativeLayout>
<TextView
android:id="@+id/tv_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="16dp"
android:textSize="16sp"
tools:text="24 Januari 2019" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="16dp"
android:layout_gravity="center">
<TextView
android:id="@+id/tv_power"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="30sp"
tools:text="27 W" />
<TextView
android:id="@+id/tv_cost"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:layout_marginStart="8dp"
tools:text="(Rp 5000)" />
</LinearLayout>
<TextView
android:id="@+id/tv_last_update"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:layout_gravity="center"
android:layout_marginTop="8dp"
android:text="@string/dummy_last_update" />
<com.github.mikephil.charting.charts.BarChart
android:id="@+id/chart2"
android:layout_width="match_parent"
android:layout_height="240dp"
android:padding="0dp"
android:layout_marginTop="16dp"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:layout_marginBottom="0dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginTop="16dp"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:text="@string/label"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:layout_marginTop="8dp"
android:textColor="@android:color/black"
android:text="@string/power_plug"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="5dp"
android:layout_marginTop="8dp"
android:src="@drawable/underline_statistic"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:layout_marginTop="16dp"
android:text="@string/total_time"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:layout_marginTop="8dp"
android:textColor="@android:color/black"
android:text="@string/dummy_total_time"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="5dp"
android:layout_marginTop="8dp"
android:src="@drawable/underline_statistic"/>
</LinearLayout>
</LinearLayout>
</ScrollView>
Я ожидаю, что моя диаграмма также поддерживает прокрутку, а не только касание.