Feat, wip[UI]: conditionnal visibility, reduced layout shifting, full screen scroll

This commit is contained in:
Mathias Boulay 2023-08-17 00:15:37 +02:00 committed by ArtDev
parent 17cce29c00
commit 04d6900bc4
7 changed files with 137 additions and 62 deletions

View File

@ -33,6 +33,7 @@ import android.view.View;
import android.view.WindowManager; import android.view.WindowManager;
import android.webkit.MimeTypeMap; import android.webkit.MimeTypeMap;
import android.widget.EditText; import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -1029,4 +1030,11 @@ public final class Tools {
Intent sendIntent = Intent.createChooser(shareIntent, "latestlog.txt"); Intent sendIntent = Intent.createChooser(shareIntent, "latestlog.txt");
context.startActivity(sendIntent); context.startActivity(sendIntent);
} }
public static int mesureTextviewHeight(TextView t) {
int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(t.getWidth(), View.MeasureSpec.AT_MOST);
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
t.measure(widthMeasureSpec, heightMeasureSpec);
return t.getMeasuredHeight();
}
} }

View File

@ -2,6 +2,7 @@ package net.kdt.pojavlaunch.fragments;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
import android.graphics.Color; import android.graphics.Color;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
@ -12,6 +13,7 @@ import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.math.MathUtils;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -35,6 +37,16 @@ import java.util.concurrent.Future;
public class SearchModFragment extends Fragment { public class SearchModFragment extends Fragment {
public static final String TAG = "SearchModFragment"; public static final String TAG = "SearchModFragment";
private View mOverlay;
private float mOverlayTopCache; // Padding cache reduce resource lookup
private final RecyclerView.OnScrollListener mOverlayPositionListener = new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
mOverlay.setY(MathUtils.clamp(mOverlay.getY() - dy, -mOverlay.getHeight(), mOverlayTopCache));
}
};
private TextView mSelectedVersion; private TextView mSelectedVersion;
private Button mSelectVersionButton; private Button mSelectVersionButton;
private EditText mSearchEditText; private EditText mSearchEditText;
@ -49,8 +61,6 @@ public class SearchModFragment extends Fragment {
private SearchFilters mSearchFilters; private SearchFilters mSearchFilters;
public SearchModFragment(){ public SearchModFragment(){
super(R.layout.fragment_mod_search); super(R.layout.fragment_mod_search);
modpackApi = new CommonApi(); modpackApi = new CommonApi();
@ -62,7 +72,9 @@ public class SearchModFragment extends Fragment {
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
// You can only access resources after attaching to current context // You can only access resources after attaching to current context
mModItemAdapter = new ModItemAdapter(getResources(), modpackApi); mModItemAdapter = new ModItemAdapter(getResources(), modpackApi);
mOverlayTopCache = getResources().getDimension(R.dimen.fragment_padding_medium);
mOverlay = view.findViewById(R.id.search_mod_overlay);
mSearchEditText = view.findViewById(R.id.search_mod_edittext); mSearchEditText = view.findViewById(R.id.search_mod_edittext);
mSearchProgressBar = view.findViewById(R.id.search_mod_progressbar); mSearchProgressBar = view.findViewById(R.id.search_mod_progressbar);
mSelectedVersion = view.findViewById(R.id.search_mod_selected_mc_version_textview); mSelectedVersion = view.findViewById(R.id.search_mod_selected_mc_version_textview);
@ -75,6 +87,8 @@ public class SearchModFragment extends Fragment {
mRecyclerview.setLayoutManager(new LinearLayoutManager(getContext())); mRecyclerview.setLayoutManager(new LinearLayoutManager(getContext()));
mRecyclerview.setAdapter(mModItemAdapter); mRecyclerview.setAdapter(mModItemAdapter);
mRecyclerview.addOnScrollListener(mOverlayPositionListener);
// Setup the expendable list behavior // Setup the expendable list behavior
mSelectVersionButton.setOnClickListener(v -> VersionSelectorDialog.open(v.getContext(), true, (id, snapshot)->{ mSelectVersionButton.setOnClickListener(v -> VersionSelectorDialog.open(v.getContext(), true, (id, snapshot)->{
mSelectedVersion.setText(id); mSelectedVersion.setText(id);
@ -93,6 +107,12 @@ public class SearchModFragment extends Fragment {
}); });
} }
@Override
public void onDestroyView() {
super.onDestroyView();
mRecyclerview.removeOnScrollListener(mOverlayPositionListener);
}
class SearchModTask implements SelfReferencingFuture.FutureInterface { class SearchModTask implements SelfReferencingFuture.FutureInterface {
private final SearchFilters mTaskFilters; private final SearchFilters mTaskFilters;

View File

@ -31,10 +31,14 @@ import net.kdt.pojavlaunch.modloaders.modpacks.models.ModDetail;
import net.kdt.pojavlaunch.modloaders.modpacks.models.ModItem; import net.kdt.pojavlaunch.modloaders.modpacks.models.ModItem;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.concurrent.Future; import java.util.concurrent.Future;
public class ModItemAdapter extends RecyclerView.Adapter<ModItemAdapter.ViewHolder> { public class ModItemAdapter extends RecyclerView.Adapter<ModItemAdapter.ViewHolder> {
private static final ModItem[] MOD_ITEMS_EMPTY = new ModItem[0]; private static final ModItem[] MOD_ITEMS_EMPTY = new ModItem[0];
/* Used when versions haven't loaded yet, default text to reduce layout shifting */
private final SimpleArrayAdapter<String> mLoadingAdapter = new SimpleArrayAdapter<>(Collections.singletonList("Loading"));
private final ModIconCache mIconCache = new ModIconCache(); private final ModIconCache mIconCache = new ModIconCache();
private ModItem[] mModItems; private ModItem[] mModItems;
private final ModpackApi mModpackApi; private final ModpackApi mModpackApi;
@ -60,7 +64,7 @@ public class ModItemAdapter extends RecyclerView.Adapter<ModItemAdapter.ViewHold
private ImageReceiver mImageReceiver; private ImageReceiver mImageReceiver;
/* Used to display available versions of the mod(pack) */ /* Used to display available versions of the mod(pack) */
private SimpleArrayAdapter<String> mVersionAdapter = new SimpleArrayAdapter<>(null); private final SimpleArrayAdapter<String> mVersionAdapter = new SimpleArrayAdapter<>(null);
public ViewHolder(View view) { public ViewHolder(View view) {
super(view); super(view);
@ -77,7 +81,7 @@ public class ModItemAdapter extends RecyclerView.Adapter<ModItemAdapter.ViewHold
mExtendedButton.getContext().getApplicationContext(), mExtendedButton.getContext().getApplicationContext(),
mModDetail, mModDetail,
mExtendedSpinner.getSelectedItemPosition())); mExtendedSpinner.getSelectedItemPosition()));
mExtendedSpinner.setAdapter(mVersionAdapter); mExtendedSpinner.setAdapter(mLoadingAdapter);
} else { } else {
if(isExtended()) closeDetailedView(); if(isExtended()) closeDetailedView();
else openDetailedView(); else openDetailedView();
@ -184,12 +188,12 @@ public class ModItemAdapter extends RecyclerView.Adapter<ModItemAdapter.ViewHold
private void openDetailedView(){ private void openDetailedView(){
mExtendedLayout.setVisibility(View.VISIBLE); mExtendedLayout.setVisibility(View.VISIBLE);
mDescription.setMaxLines(99); mDescription.setMaxLines(99);
mExtendedLayout.post(() -> {
// We need to align to the longer section // We need to align to the longer section
ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) mExtendedLayout.getLayoutParams(); int futureBottom = mDescription.getBottom() + Tools.mesureTextviewHeight(mDescription) - mDescription.getHeight();
params.topToBottom = mDescription.getBottom() > mIconView.getBottom() ? R.id.mod_body_textview : R.id.mod_thumbnail_imageview; ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) mExtendedLayout.getLayoutParams();
mExtendedLayout.setLayoutParams(params); params.topToBottom = futureBottom > mIconView.getBottom() ? R.id.mod_body_textview : R.id.mod_thumbnail_imageview;
}); mExtendedLayout.setLayoutParams(params);
} }
private void closeDetailedView(){ private void closeDetailedView(){
@ -199,7 +203,7 @@ public class ModItemAdapter extends RecyclerView.Adapter<ModItemAdapter.ViewHold
private void setDetailedStateDefault() { private void setDetailedStateDefault() {
mExtendedButton.setEnabled(false); mExtendedButton.setEnabled(false);
mExtendedSpinner.setAdapter(null); mExtendedSpinner.setAdapter(mLoadingAdapter);
mExtendedErrorTextView.setVisibility(View.GONE); mExtendedErrorTextView.setVisibility(View.GONE);
openDetailedView(); openDetailedView();
} }

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/_2sdp" />
<padding
android:bottom="@dimen/padding_small"
android:left="@dimen/padding_small"
android:right="@dimen/padding_small"
android:top="@dimen/padding_small" />
<solid android:color="@color/background_overlay" />
</shape>

View File

@ -6,75 +6,106 @@
android:background="@color/background_app" android:background="@color/background_app"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:paddingHorizontal="@dimen/fragment_padding_medium" android:paddingHorizontal="@dimen/fragment_padding_medium"
android:paddingVertical="@dimen/fragment_padding_medium"> >
<EditText
android:id="@+id/search_mod_edittext"
<!--
Cosmetic layout to have a better scrolling separation
-->
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/search_mod_overlay"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/background_line" android:translationZ="10dp"
android:textSize="@dimen/_13ssp" android:background="@drawable/background_overlay"
android:layout_marginBottom="@dimen/padding_large" android:paddingHorizontal="@dimen/padding_medium"
android:paddingVertical="@dimen/padding_medium"
android:layout_marginTop="@dimen/fragment_padding_medium"
android:layout_marginHorizontal="@dimen/fragment_padding_medium"
android:ems="10"
android:hint="@string/hint_search_modpack"
android:inputType="textPersonName"
android:paddingHorizontal="@dimen/padding_heavy"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
/>
<ProgressBar
android:id="@+id/search_mod_progressbar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="true"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@+id/search_mod_edittext"
tools:layout_editor_absoluteX="13dp" />
<TextView >
android:id="@+id/search_mod_mc_version_textview" <!-- Search text -->
android:layout_width="wrap_content" <EditText
android:layout_height="wrap_content" android:id="@+id/search_mod_edittext"
android:text="@string/profiles_profile_version" android:layout_width="match_parent"
app:layout_constraintBottom_toTopOf="@+id/search_mod_selected_mc_version_textview" android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="@+id/search_mod_selected_mc_version_textview" /> android:background="@drawable/background_line"
android:textSize="@dimen/_13ssp"
android:translationZ="2dp"
<TextView android:ems="10"
android:id="@+id/search_mod_selected_mc_version_textview" android:hint="@string/hint_search_modpack"
android:layout_width="0dp" android:inputType="textPersonName"
android:layout_height="wrap_content" android:paddingHorizontal="@dimen/padding_heavy"
android:layout_marginVertical="32dp" app:layout_constraintTop_toTopOf="parent"
android:layout_marginEnd="@dimen/padding_medium" />
<ProgressBar
android:id="@+id/search_mod_progressbar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="true"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@+id/search_mod_edittext"
tools:layout_editor_absoluteX="13dp" />
<!-- Version filter -->
<TextView
android:id="@+id/search_mod_mc_version_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/profiles_profile_version"
app:layout_constraintBottom_toTopOf="@+id/search_mod_selected_mc_version_textview"
app:layout_constraintStart_toStartOf="@+id/search_mod_selected_mc_version_textview" />
<TextView
android:id="@+id/search_mod_selected_mc_version_textview"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginVertical="32dp"
android:layout_marginEnd="@dimen/padding_medium"
android:background="@drawable/background_line"
android:hint="@string/version_select_hint"
android:paddingHorizontal="@dimen/padding_heavy"
android:textSize="@dimen/_13ssp"
app:layout_constraintEnd_toStartOf="@+id/search_mod_mc_version_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/search_mod_progressbar" />
<Button
android:id="@+id/search_mod_mc_version_button"
android:layout_width="@dimen/_72sdp"
android:layout_height="0dp"
android:text="@string/global_select"
app:layout_constraintBottom_toBottomOf="@+id/search_mod_selected_mc_version_textview"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/search_mod_selected_mc_version_textview" />
</androidx.constraintlayout.widget.ConstraintLayout>
android:background="@drawable/background_line"
android:hint="@string/version_select_hint"
android:paddingHorizontal="@dimen/padding_heavy"
android:textSize="@dimen/_13ssp"
app:layout_constraintEnd_toStartOf="@+id/search_mod_mc_version_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/search_mod_progressbar" />
<Button
android:id="@+id/search_mod_mc_version_button"
android:layout_width="@dimen/_72sdp"
android:layout_height="0dp"
android:text="@string/global_select"
app:layout_constraintBottom_toBottomOf="@+id/search_mod_selected_mc_version_textview"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/search_mod_selected_mc_version_textview" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/search_mod_list" android:id="@+id/search_mod_list"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="match_parent"
android:paddingTop="@dimen/_65sdp"
android:clipToPadding="false"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/search_mod_selected_mc_version_textview" app:layout_constraintTop_toTopOf="parent"
/> />
<TextView <TextView

View File

@ -76,7 +76,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout="@layout/view_mod_extended" android:layout="@layout/view_mod_extended"
app:layout_constraintTop_toBottomOf="@id/mod_body_textview"/> app:layout_constraintTop_toBottomOf="@id/mod_thumbnail_imageview"/>

View File

@ -5,6 +5,7 @@
<color name="minebutton_color">#57CC33</color> <color name="minebutton_color">#57CC33</color>
<color name="background_app">#181818</color> <color name="background_app">#181818</color>
<color name="background_overlay">#464646</color>
<color name="background_status_bar">#242424</color> <color name="background_status_bar">#242424</color>
<color name="background_bottom_bar">#232323</color> <color name="background_bottom_bar">#232323</color>