From fe92d4e27b3d7a56e62ad35ce4c972e5319dd2af Mon Sep 17 00:00:00 2001 From: Astatin3 <77305074+Astatin3@users.noreply.github.com> Date: Tue, 23 Jul 2024 12:27:45 -0600 Subject: [PATCH] Write ReorderableTableLayout --- .../scoutingapp2025/scoutingData/fields.java | 15 +- .../types/input/dropdownType.java | 1 + .../types/input/inputType.java | 1 + .../types/input/sliderType.java | 1 + .../input/{notesType.java => textType.java} | 8 +- .../scoutingapp2025/ui/data/fieldsView.java | 96 +++++++++++- .../utility/ReorderableTableLayout.java | 145 ++++++++++++++++++ app/src/main/res/layout/fragment_data.xml | 4 +- 8 files changed, 252 insertions(+), 19 deletions(-) rename app/src/main/java/com/astatin3/scoutingapp2025/types/input/{notesType.java => textType.java} (95%) create mode 100644 app/src/main/java/com/astatin3/scoutingapp2025/utility/ReorderableTableLayout.java diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/scoutingData/fields.java b/app/src/main/java/com/astatin3/scoutingapp2025/scoutingData/fields.java index 0c87682..53ffe98 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/scoutingData/fields.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/scoutingData/fields.java @@ -2,8 +2,9 @@ package com.astatin3.scoutingapp2025.scoutingData; import com.astatin3.scoutingapp2025.types.input.dropdownType; import com.astatin3.scoutingapp2025.types.input.inputType; -import com.astatin3.scoutingapp2025.types.input.notesType; +import com.astatin3.scoutingapp2025.types.input.textType; import com.astatin3.scoutingapp2025.types.input.sliderType; +import com.astatin3.scoutingapp2025.types.input.textType; import com.astatin3.scoutingapp2025.utility.fileEditor; import com.astatin3.scoutingapp2025.utility.BuiltByteParser; import com.astatin3.scoutingapp2025.utility.ByteBuilder; @@ -19,27 +20,27 @@ public class fields { public static final inputType[][] default_match_fields = new inputType[][] { { new sliderType("How good is robot", 5, 1, 10), - new notesType("notes", ""), + new textType("notes", ""), },{ new sliderType("How good is robot", 5, 1, 10), new sliderType("Test", 128, 64, 256), - new notesType("notes", ""), + new textType("notes", ""), },{ new sliderType("How good is robot", 5, 5, 10), new sliderType("Test", 128, 64, 256), new dropdownType("test-dropdown", new String[]{"Test1", "test2", "Three"}, 1), - new notesType("notes", ""), + new textType("notes", ""), } }; public static final inputType[][] default_pit_fields = new inputType[][] { { new sliderType("How good is robot", 5, 0, 10), - new notesType("notes", ""), + new textType("notes", ""), },{ new sliderType("How good is robot", 5, 0, 10), new sliderType("Test", 1, 0, 10), - new notesType("notes", ""), + new textType("notes", ""), } }; @@ -107,7 +108,7 @@ public class fields { t = new dropdownType(); break; case inputType.notesType: - t = new notesType(); + t = new textType(); break; } diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/types/input/dropdownType.java b/app/src/main/java/com/astatin3/scoutingapp2025/types/input/dropdownType.java index 4595c11..2f47d24 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/types/input/dropdownType.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/types/input/dropdownType.java @@ -40,6 +40,7 @@ public class dropdownType extends inputType { public dataType.valueTypes getValueType(){return dataType.valueTypes.NUM;} public Object get_fallback_value(){return 0;} public dropdownType(){}; + public String get_type_name(){return "Dropdown";} public dropdownType(String name, String[] text_options, int defaultSelIndex){ super(name); this.text_options = text_options; diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/types/input/inputType.java b/app/src/main/java/com/astatin3/scoutingapp2025/types/input/inputType.java index c5e4a40..667376d 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/types/input/inputType.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/types/input/inputType.java @@ -43,5 +43,6 @@ public abstract class inputType { public abstract dataType getViewValue(); public abstract void add_individual_view(LinearLayout parent, dataType data); public abstract void add_compiled_view(LinearLayout parent, dataType[] data); + public abstract String get_type_name(); } \ No newline at end of file diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/types/input/sliderType.java b/app/src/main/java/com/astatin3/scoutingapp2025/types/input/sliderType.java index 33ffb61..8152f20 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/types/input/sliderType.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/types/input/sliderType.java @@ -34,6 +34,7 @@ public class sliderType extends inputType { public dataType.valueTypes getValueType(){return dataType.valueTypes.NUM;} public Object get_fallback_value(){return 0;} public sliderType(){}; + public String get_type_name(){return "Slider";} public sliderType(String name, int defaultValue, int min, int max){ super(name); this.default_value = defaultValue; diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/types/input/notesType.java b/app/src/main/java/com/astatin3/scoutingapp2025/types/input/textType.java similarity index 95% rename from app/src/main/java/com/astatin3/scoutingapp2025/types/input/notesType.java rename to app/src/main/java/com/astatin3/scoutingapp2025/types/input/textType.java index c046860..08443df 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/types/input/notesType.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/types/input/textType.java @@ -12,7 +12,6 @@ import android.widget.LinearLayout; import android.widget.TextView; import com.astatin3.scoutingapp2025.types.data.dataType; -import com.astatin3.scoutingapp2025.types.data.intType; import com.astatin3.scoutingapp2025.types.data.stringType; import com.astatin3.scoutingapp2025.utility.BuiltByteParser; import com.astatin3.scoutingapp2025.utility.ByteBuilder; @@ -20,16 +19,17 @@ import com.astatin3.scoutingapp2025.utility.ByteBuilder; import java.util.ArrayList; import java.util.function.Function; -public class notesType extends inputType { +public class textType extends inputType { public int get_byte_id() {return notesType;} public inputTypes getInputType(){return inputTypes.NOTES_INPUT;} public dataType.valueTypes getValueType(){return dataType.valueTypes.STRING;} public Object get_fallback_value(){return "";} - public notesType(){}; - public notesType(String name, String default_text){ + public textType(){} + public textType(String name, String default_text){ super(name); this.default_value = default_text; } + public String get_type_name(){return "Text";} public byte[] encode() throws ByteBuilder.buildingException { ByteBuilder bb = new ByteBuilder(); bb.addString(name); diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/ui/data/fieldsView.java b/app/src/main/java/com/astatin3/scoutingapp2025/ui/data/fieldsView.java index 2b8e4f9..fc40bff 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/ui/data/fieldsView.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/ui/data/fieldsView.java @@ -2,6 +2,7 @@ package com.astatin3.scoutingapp2025.ui.data; import android.content.Context; import android.util.AttributeSet; +import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.TableLayout; import android.widget.TableRow; @@ -9,11 +10,15 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.constraintlayout.widget.ConstraintLayout; +import androidx.recyclerview.widget.RecyclerView; import com.astatin3.scoutingapp2025.databinding.FragmentDataBinding; import com.astatin3.scoutingapp2025.scoutingData.fields; import com.astatin3.scoutingapp2025.types.input.inputType; +import java.util.ArrayList; +import java.util.List; + public class fieldsView extends ConstraintLayout { public fieldsView(@NonNull Context context) { super(context); @@ -24,6 +29,7 @@ public class fieldsView extends ConstraintLayout { FragmentDataBinding binding; String filename; private static final int background_color = 0x5000ff00; + private static final int unfocused_background_color = 0x2000ff00; inputType[][] values; @@ -32,10 +38,12 @@ public class fieldsView extends ConstraintLayout { binding.fieldsSelectButtons.setVisibility(VISIBLE); binding.addButton.setVisibility(GONE); + binding.fieldsArea.setReorderingEnabled(false); binding.fieldsArea.removeAllViews(); + binding.fieldsArea.setStretchAllColumns(true); binding.fieldsSelectButtons.bringToFront(); - binding.fieldsArea.setStretchAllColumns(true); +// binding.fieldsArea.setStretchAllColumns(true); binding.matchScoutingButton.setOnClickListener(v -> { binding.fieldsSelectButtons.setVisibility(GONE); @@ -52,8 +60,37 @@ public class fieldsView extends ConstraintLayout { }); } + private TextView createTextView(String text) { + TextView textView = new TextView(getContext()); + textView.setText(text); + textView.setPadding(10, 10, 10, 10); + return textView; + } + private void load_field_menu() { values = fields.load(filename); + binding.fieldsArea.bringToFront(); + binding.fieldsArea.removeAllViews(); + binding.fieldsArea.setReorderingEnabled(false); + if(values == null) return; + +// List rows = new ArrayList<>(); +// +//// Create and add rows +// TableRow row1 = new TableRow(getContext()); +// row1.addView(createTextView("Cell 1,1")); +// row1.addView(createTextView("Cell 1,2")); +// rows.add(row1); +// +// TableRow row2 = new TableRow(getContext()); +// row2.addView(createTextView("Cell 2,1")); +// row2.addView(createTextView("Cell 2,2")); +// rows.add(row2); +// +//// Set the rows +// binding.fieldsArea.setRows(rows); +// +//// Or add rows individually for(int i = 0; i < values.length; i++){ @@ -66,12 +103,11 @@ public class fieldsView extends ConstraintLayout { rowParams.setMargins(20,20,20,20); tr.setLayoutParams(rowParams); tr.setPadding(20,20,20,20); - binding.fieldsArea.addView(tr); - +// tr.setMinimumWidth(); tr.setBackgroundColor(background_color); TextView tv = new TextView(getContext()); - tv.setText("v" + i); + tv.setText("v" + i + "\n eee"); tv.setTextSize(20); tr.addView(tv); @@ -80,16 +116,64 @@ public class fieldsView extends ConstraintLayout { tv.setTextSize(16); tr.addView(tv); + binding.fieldsArea.addView(tr); + // frcTeam finalTeam = team; int fi = i; tr.setOnClickListener(v -> { // loadTeam(finalTeam, latestSettings.settings.get_compiled_mode()); - load_fields(values[fi]); + display_fields(values[fi]); }); } } - private void load_fields(inputType[] version_values) { + private void display_fields(inputType[] version_values) { + binding.fieldsArea.removeAllViews(); + binding.fieldsArea.setReorderingEnabled(true); + +// ArrayList rows = new ArrayList<>(); + + for(int i = 0; i < version_values.length; i++){ + TableRow tr = new TableRow(getContext()); + TableLayout.LayoutParams rowParams = new TableLayout.LayoutParams( + FrameLayout.LayoutParams.WRAP_CONTENT, + FrameLayout.LayoutParams.WRAP_CONTENT + ); + + rowParams.setMargins(20,20,20,20); + tr.setLayoutParams(rowParams); + tr.setPadding(20,20,20,20); + tr.setBackgroundColor(unfocused_background_color); + + TextView tv = new TextView(getContext()); + tv.setText(version_values[i].get_type_name()); + tv.setTextSize(12); + tr.addView(tv); + + tv = new TextView(getContext()); + tv.setText(version_values[i].name); + tv.setTextSize(20); + tr.addView(tv); +// rows.add(tr); +// tr.addView(tv); + binding.fieldsArea.addView(tr); + tr.setOnClickListener(v -> { + + trHighlight(tr); + }); + } + +// binding.fieldsArea.setSlidingEnabled(true); +// binding.fieldsArea.setRows(rows); + } + + private void trHighlight(TableRow self){ + for(int i = 0; i < binding.teamsArea.getChildCount(); i++){ + TableRow child = (TableRow) binding.teamsArea.getChildAt(i); + child.setBackgroundColor(unfocused_background_color); + } + + self.setBackgroundColor(background_color); } } diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/utility/ReorderableTableLayout.java b/app/src/main/java/com/astatin3/scoutingapp2025/utility/ReorderableTableLayout.java new file mode 100644 index 0000000..5d07602 --- /dev/null +++ b/app/src/main/java/com/astatin3/scoutingapp2025/utility/ReorderableTableLayout.java @@ -0,0 +1,145 @@ +package com.astatin3.scoutingapp2025.utility; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ValueAnimator; +import android.content.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.ItemTouchHelper; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import android.animation.ObjectAnimator; +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.widget.TableLayout; +import android.widget.TableRow; + +import java.util.ArrayList; +import java.util.List; + +public class ReorderableTableLayout extends TableLayout { + private boolean reorderingEnabled = false; + private int draggedRowIndex = -1; + private float lastY; + private List originalRows; + private int rowHeight; + + public ReorderableTableLayout(Context context) { + super(context); + init(); + } + + public ReorderableTableLayout(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + private void init() { + originalRows = new ArrayList<>(); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (!reorderingEnabled) { + return super.onInterceptTouchEvent(ev); + } + + switch (ev.getAction()) { + case MotionEvent.ACTION_DOWN: + lastY = ev.getY(); + draggedRowIndex = getRowIndexAtY(lastY); + if (draggedRowIndex != -1) { + View draggedRow = getChildAt(draggedRowIndex); + rowHeight = draggedRow.getHeight(); + saveOriginalOrder(); + return true; + } + break; + } + return super.onInterceptTouchEvent(ev); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (!reorderingEnabled || draggedRowIndex == -1) { + return super.onTouchEvent(event); + } + + switch (event.getAction()) { + case MotionEvent.ACTION_MOVE: + float currentY = event.getY(); + int targetIndex = getRowIndexAtY(currentY); + if (targetIndex != -1 && targetIndex != draggedRowIndex) { + updateRowOrder(draggedRowIndex, targetIndex); + draggedRowIndex = targetIndex; + } + lastY = currentY; + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + draggedRowIndex = -1; + break; + } + return true; + } + + private int getRowIndexAtY(float y) { + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + if (y >= child.getTop() && y <= child.getBottom()) { + return i; + } + } + return -1; + } + + private void saveOriginalOrder() { + originalRows.clear(); + for (int i = 0; i < getChildCount(); i++) { + originalRows.add(getChildAt(i)); + } + } + + private void updateRowOrder(int fromIndex, int toIndex) { + if (fromIndex < toIndex) { + for (int i = fromIndex; i < toIndex; i++) { + Collections.swap(originalRows, i, i + 1); + } + } else { + for (int i = fromIndex; i > toIndex; i--) { + Collections.swap(originalRows, i, i - 1); + } + } + + removeAllViewsInLayout(); + for (View view : originalRows) { + addViewInLayout(view, -1, view.getLayoutParams(), true); + } + requestLayout(); + invalidate(); + } + + public void setReorderingEnabled(boolean enabled) { + reorderingEnabled = enabled; + } + + public List getReorderedIndexes() { + List reorderedIndexes = new ArrayList<>(); + for (View view : originalRows) { + reorderedIndexes.add(indexOfChild(view)); + } + return reorderedIndexes; + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_data.xml b/app/src/main/res/layout/fragment_data.xml index 70f4a68..f4cf29f 100644 --- a/app/src/main/res/layout/fragment_data.xml +++ b/app/src/main/res/layout/fragment_data.xml @@ -204,12 +204,12 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - +