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:
@@ -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;
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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();
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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 >
|
||||
|
@@ -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>
|
||||
|
Reference in New Issue
Block a user