1
0
mirror of https://github.com/oliexdev/openScale.git synced 2025-08-19 06:51:57 +02:00

replaced table view with sticky header and column table view.

This commit is contained in:
oliexdev
2023-01-29 13:09:20 +01:00
parent af7d74aed4
commit 8047e9e252
9 changed files with 1601 additions and 218 deletions

View File

@@ -23,6 +23,7 @@ import android.widget.EditText;
import com.health.openscale.R;
import com.health.openscale.core.datatypes.ScaleMeasurement;
import com.health.openscale.gui.utils.ColorUtil;
public class CommentMeasurementView extends MeasurementView {
// Don't change key value, it may be stored persistent in preferences
@@ -71,6 +72,9 @@ public class CommentMeasurementView extends MeasurementView {
state.putString(getKey(), comment);
}
@Override
public int getColor() { return ColorUtil.COLOR_GRAY; };
@Override
public String getValueAsString(boolean withUnit) {
return comment;

View File

@@ -23,6 +23,7 @@ import android.widget.DatePicker;
import com.health.openscale.R;
import com.health.openscale.core.datatypes.ScaleMeasurement;
import com.health.openscale.gui.utils.ColorUtil;
import java.text.DateFormat;
import java.util.Calendar;
@@ -88,6 +89,9 @@ public class DateMeasurementView extends MeasurementView {
state.putLong(getKey(), date.getTime());
}
@Override
public int getColor() { return ColorUtil.COLOR_GRAY; };
@Override
public String getValueAsString(boolean withUnit) {
return dateFormat.format(date);

View File

@@ -358,6 +358,8 @@ public abstract class MeasurementView extends TableLayout {
return background.getColor();
}
abstract public int getColor();
protected void showEvaluatorRow(boolean show) {
if (show) {
evaluatorRow.setVisibility(View.VISIBLE);

View File

@@ -22,6 +22,7 @@ import android.widget.TimePicker;
import com.health.openscale.R;
import com.health.openscale.core.datatypes.ScaleMeasurement;
import com.health.openscale.gui.utils.ColorUtil;
import java.text.DateFormat;
import java.util.Calendar;
@@ -89,6 +90,9 @@ public class TimeMeasurementView extends MeasurementView {
state.putLong(getKey(), time.getTime());
}
@Override
public int getColor() { return ColorUtil.COLOR_GRAY; };
@Override
public String getValueAsString(boolean withUnit) {
return timeFormat.format(time);

View File

@@ -25,6 +25,7 @@ import com.health.openscale.R;
import com.health.openscale.core.OpenScale;
import com.health.openscale.core.datatypes.ScaleMeasurement;
import com.health.openscale.core.datatypes.ScaleUser;
import com.health.openscale.gui.utils.ColorUtil;
import java.util.ArrayList;
@@ -80,6 +81,9 @@ public class UserMeasurementView extends MeasurementView {
state.putInt(getKey(), userId);
}
@Override
public int getColor() { return ColorUtil.COLOR_GRAY; };
@Override
public String getValueAsString(boolean withUnit) {
return openScale.getScaleUser(userId).getUserName();

View File

@@ -15,50 +15,41 @@
*/
package com.health.openscale.gui.table;
import android.content.res.Configuration;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.SpannableStringBuilder;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TableRow;
import android.widget.TextView;
import androidx.activity.OnBackPressedCallback;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.navigation.Navigation;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.health.openscale.R;
import com.health.openscale.core.OpenScale;
import com.health.openscale.core.datatypes.ScaleMeasurement;
import com.health.openscale.gui.measurement.DateMeasurementView;
import com.health.openscale.gui.measurement.MeasurementEntryFragment;
import com.health.openscale.gui.measurement.MeasurementView;
import com.health.openscale.gui.measurement.TimeMeasurementView;
import com.health.openscale.gui.measurement.UserMeasurementView;
import com.health.openscale.gui.utils.ColorUtil;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import static android.util.TypedValue.COMPLEX_UNIT_DIP;
public class TableFragment extends Fragment {
private View tableView;
private LinearLayout tableHeaderView;
private RecyclerView recyclerView;
private MeasurementsAdapter adapter;
private LinearLayoutManager layoutManager;
private StickyHeaderTableView tableDataView;
private List<MeasurementView> measurementViews;
private List<ScaleMeasurement> scaleMeasurementList;
private ArrayList<Drawable> iconList;
public TableFragment() {
@@ -68,25 +59,34 @@ public class TableFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
tableView = inflater.inflate(R.layout.fragment_table, container, false);
tableHeaderView = tableView.findViewById(R.id.tableHeaderView);
recyclerView = tableView.findViewById(R.id.tableDataView);
tableDataView = tableView.findViewById(R.id.tableDataView);
recyclerView.setHasFixedSize(true);
layoutManager = new LinearLayoutManager(getContext());
recyclerView.setLayoutManager(layoutManager);
recyclerView.addItemDecoration(new DividerItemDecoration(
recyclerView.getContext(), layoutManager.getOrientation()));
adapter = new MeasurementsAdapter();
recyclerView.setAdapter(adapter);
tableDataView.setOnTableCellClickListener(new StickyHeaderTableView.OnTableCellClickListener() {
@Override
public void onTableCellClicked(int rowPosition, int columnPosition) {
if (rowPosition > 0) {
TableFragmentDirections.ActionNavTableToNavDataentry action = TableFragmentDirections.actionNavTableToNavDataentry();
action.setMeasurementId(scaleMeasurementList.get(rowPosition-1).getId());
action.setMode(MeasurementEntryFragment.DATA_ENTRY_MODE.VIEW);
Navigation.findNavController(getActivity(), R.id.nav_host_fragment).navigate(action);
}
}
});
measurementViews = MeasurementView.getMeasurementList(
getContext(), MeasurementView.DateTimeOrder.FIRST);
for (MeasurementView measurement : measurementViews) {
measurement.setUpdateViews(false);
iconList = new ArrayList<>();
for (MeasurementView measurementView : measurementViews) {
if (!measurementView.isVisible() || measurementView instanceof UserMeasurementView || measurementView instanceof TimeMeasurementView) {
continue;
}
// measurementView.setUpdateViews(false);
measurementView.getIcon().setColorFilter(measurementView.getColor(), PorterDuff.Mode.SRC_ATOP);
iconList.add(measurementView.getIcon());
}
OpenScale.getInstance().getScaleMeasurementsLiveData().observe(getViewLifecycleOwner(), new Observer<List<ScaleMeasurement>>() {
@@ -111,177 +111,53 @@ public class TableFragment extends Fragment {
public void updateOnView(List<ScaleMeasurement> scaleMeasurementList)
{
tableHeaderView.removeAllViews();
this.scaleMeasurementList = scaleMeasurementList;
final int iconHeight = pxImageDp(20);
ArrayList<MeasurementView> visibleMeasurements = new ArrayList<>();
Object[][] tableData = new Object[scaleMeasurementList.size()+1][iconList.size()];
for (MeasurementView measurement : measurementViews) {
if (!measurement.isVisible() || measurement instanceof UserMeasurementView) {
continue;
}
ImageView headerIcon = new ImageView(tableView.getContext());
headerIcon.setImageDrawable(measurement.getIcon());
headerIcon.setColorFilter(ColorUtil.getTintColor(tableView.getContext()));
headerIcon.setLayoutParams(new TableRow.LayoutParams(0, iconHeight, 1));
headerIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
tableHeaderView.addView(headerIcon);
visibleMeasurements.add(measurement);
// add header icons to the first table data row
for (int j=0; j<iconList.size(); j++) {
tableData[0][j] = iconList.get(j);
}
adapter.setMeasurements(visibleMeasurements, scaleMeasurementList);
int i = 0;
for (ScaleMeasurement scaleMeasurement : scaleMeasurementList) {
int j=0;
ScaleMeasurement prevScaleMeasurement = null;
if ((i+1) < scaleMeasurementList.size()) {
prevScaleMeasurement = scaleMeasurementList.get(i+1);
}
for (MeasurementView measurementView : measurementViews) {
if (!measurementView.isVisible() || measurementView instanceof UserMeasurementView || measurementView instanceof TimeMeasurementView) {
continue;
}
if (measurementView instanceof DateMeasurementView) {
String strDateTime = (DateFormat.getDateInstance(DateFormat.SHORT).format(scaleMeasurement.getDateTime()) +
" (" + new SimpleDateFormat("EE").format(scaleMeasurement.getDateTime()) + ")\n"+
DateFormat.getTimeInstance(DateFormat.SHORT).format(scaleMeasurement.getDateTime()));
tableData[i+1][j] = strDateTime;
} else {
measurementView.loadFrom(scaleMeasurement, prevScaleMeasurement);
SpannableStringBuilder string = new SpannableStringBuilder();
string.append(measurementView.getValueAsString(false));
measurementView.appendDiffValue(string, true);
tableData[i+1][j] = string.toString();
}
j++;
}
i++;
}
tableDataView.setData(tableData);
}
private int pxImageDp(float dp) {
return (int)(dp * getResources().getDisplayMetrics().density + 0.5f);
}
private class MeasurementsAdapter extends RecyclerView.Adapter<MeasurementsAdapter.ViewHolder> {
public static final int VIEW_TYPE_MEASUREMENT = 0;
public static final int VIEW_TYPE_YEAR = 1;
public class ViewHolder extends RecyclerView.ViewHolder {
public LinearLayout measurementView;
public ViewHolder(LinearLayout view) {
super(view);
measurementView = view;
}
}
private List<MeasurementView> visibleMeasurements;
private List<ScaleMeasurement> scaleMeasurements;
public void setMeasurements(List<MeasurementView> visibleMeasurements,
List<ScaleMeasurement> scaleMeasurements) {
this.visibleMeasurements = visibleMeasurements;
this.scaleMeasurements = new ArrayList<>(scaleMeasurements.size() + 10);
Calendar calendar = Calendar.getInstance();
if (!scaleMeasurements.isEmpty()) {
calendar.setTime(scaleMeasurements.get(0).getDateTime());
}
calendar.set(calendar.get(Calendar.YEAR), 0, 1, 0, 0, 0);
calendar.set(calendar.MILLISECOND, 0);
// Copy all measurements from input parameter to member variable and insert
// an extra "null" entry when the year changes.
Date yearStart = calendar.getTime();
for (int i = 0; i < scaleMeasurements.size(); ++i) {
final ScaleMeasurement measurement = scaleMeasurements.get(i);
if (measurement.getDateTime().before(yearStart)) {
this.scaleMeasurements.add(null);
Calendar newCalendar = Calendar.getInstance();
newCalendar.setTime(measurement.getDateTime());
calendar.set(Calendar.YEAR, newCalendar.get(Calendar.YEAR));
yearStart = calendar.getTime();
}
this.scaleMeasurements.add(measurement);
}
notifyDataSetChanged();
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LinearLayout row = new LinearLayout(getContext());
row.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
final int screenSize = getResources().getConfiguration()
.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;
final boolean isSmallScreen =
screenSize != Configuration.SCREENLAYOUT_SIZE_XLARGE
&& screenSize != Configuration.SCREENLAYOUT_SIZE_LARGE;
final int count = viewType == VIEW_TYPE_YEAR ? 1 : visibleMeasurements.size();
for (int i = 0; i < count; ++i) {
TextView column = new TextView(getContext());
column.setLayoutParams(new LinearLayout.LayoutParams(
0, ViewGroup.LayoutParams.WRAP_CONTENT, 1));
if (viewType == VIEW_TYPE_MEASUREMENT) {
column.setMinLines(2);
column.setGravity(Gravity.CENTER_HORIZONTAL);
if (isSmallScreen) {
column.setTextSize(COMPLEX_UNIT_DIP, 9);
}
}
else {
column.setPadding(0, 10, 0, 10);
column.setGravity(Gravity.CENTER);
column.setTextSize(COMPLEX_UNIT_DIP, 16);
}
row.addView(column);
}
return new ViewHolder(row);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
LinearLayout row = holder.measurementView;
final ScaleMeasurement measurement = scaleMeasurements.get(position);
if (measurement == null) {
ScaleMeasurement nextMeasurement = scaleMeasurements.get(position + 1);
Calendar calendar = Calendar.getInstance();
calendar.setTime(nextMeasurement.getDateTime());
TextView column = (TextView) row.getChildAt(0);
column.setText(String.format("%d", calendar.get(Calendar.YEAR)));
return;
}
ScaleMeasurement prevMeasurement = null;
if (position + 1 < scaleMeasurements.size()) {
prevMeasurement = scaleMeasurements.get(position + 1);
if (prevMeasurement == null) {
prevMeasurement = scaleMeasurements.get(position + 2);
}
}
// Fill view with data
for (int i = 0; i < visibleMeasurements.size(); ++i) {
final MeasurementView view = visibleMeasurements.get(i);
view.loadFrom(measurement, prevMeasurement);
SpannableStringBuilder string = new SpannableStringBuilder();
string.append(view.getValueAsString(false));
view.appendDiffValue(string, true);
TextView column = (TextView) row.getChildAt(i);
column.setText(string);
}
row.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
TableFragmentDirections.ActionNavTableToNavDataentry action = TableFragmentDirections.actionNavTableToNavDataentry();
action.setMeasurementId(measurement.getId());
action.setMode(MeasurementEntryFragment.DATA_ENTRY_MODE.VIEW);
Navigation.findNavController(getActivity(), R.id.nav_host_fragment).navigate(action);
}
});
}
@Override
public int getItemCount() {
return scaleMeasurements == null ? 0 : scaleMeasurements.size();
}
@Override
public int getItemViewType(int position) {
return scaleMeasurements.get(position) != null ? VIEW_TYPE_MEASUREMENT : VIEW_TYPE_YEAR;
}
}
}

View File

@@ -1,24 +1,27 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/tableHeaderView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingBottom="5dp"
android:paddingTop="5dp" />
<View
android:layout_width="match_parent"
android:layout_height="2dp"
android:background="?android:attr/listDivider" />
<androidx.recyclerview.widget.RecyclerView
<com.health.openscale.gui.table.StickyHeaderTableView
android:id="@+id/tableDataView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical" />
</LinearLayout>
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:shtv_cellPadding="10dp"
app:shtv_dividerColor="?android:colorBackground"
app:shtv_dividerThickness="1dp"
app:shtv_headerCellFillColor="?android:colorBackground"
app:shtv_contentCellFillColor="?android:colorBackground"
app:shtv_is2DScrollEnabled="true"
app:shtv_isDisplayLeftHeadersVertically="false"
app:shtv_isWrapHeightOfEachRow="true"
app:shtv_isWrapWidthOfEachColumn="true"
app:shtv_textHeaderColor="?android:colorForeground"
app:shtv_textHeaderSize="14dp"
app:shtv_textLabelColor="?android:colorForeground"
app:shtv_textLabelSize="14dp" />
</androidx.constraintlayout.widget.ConstraintLayout >

View File

@@ -37,4 +37,26 @@
<item name="positiveButtonText">@android:string/ok</item>
<item name="negativeButtonText">@android:string/cancel</item>
</style>
<declare-styleable name="StickyHeaderTableView">
<attr name="shtv_textLabelColor" format="color" />
<attr name="shtv_textLabelSize" format="dimension" />
<attr name="shtv_textHeaderColor" format="color" />
<attr name="shtv_textHeaderSize" format="dimension" />
<attr name="shtv_dividerColor" format="dimension" />
<attr name="shtv_dividerThickness" format="dimension" />
<attr name="shtv_contentCellFillColor" format="color" />
<attr name="shtv_headerCellFillColor" format="color" />
<attr name="shtv_cellPadding" format="dimension" />
<attr name="shtv_isDisplayLeftHeadersVertically" format="boolean" />
<attr name="shtv_isWrapHeightOfEachRow" format="boolean" />
<attr name="shtv_isWrapWidthOfEachColumn" format="boolean" />
<attr name="shtv_is2DScrollEnabled" format="boolean" />
</declare-styleable>
</resources>