Optimize folder ux

release
jhengazuki 7 months ago
parent d6a5135db5
commit 83012d9ac1
  1. 2
      app/src/leanback/java/com/fongmi/android/tv/ui/activity/CollectActivity.java
  2. 19
      app/src/leanback/java/com/fongmi/android/tv/ui/activity/VodActivity.java
  3. 23
      app/src/leanback/java/com/fongmi/android/tv/ui/base/BaseFragment.java
  4. 5
      app/src/leanback/java/com/fongmi/android/tv/ui/custom/CustomVerticalGridView.java
  5. 20
      app/src/leanback/java/com/fongmi/android/tv/ui/fragment/CollectFragment.java
  6. 90
      app/src/leanback/java/com/fongmi/android/tv/ui/fragment/TypeFragment.java
  7. 95
      app/src/leanback/java/com/fongmi/android/tv/ui/fragment/VodFragment.java
  8. 11
      app/src/leanback/res/layout/fragment_folder.xml
  9. 10
      app/src/leanback/res/layout/fragment_vod.xml

@ -158,7 +158,7 @@ public class CollectActivity extends BaseActivity {
if (child == null) return;
mOldView = child.itemView;
mOldView.setActivated(true);
App.post(mRunnable, 200);
App.post(mRunnable, 100);
}
private final Runnable mRunnable = new Runnable() {

@ -26,7 +26,7 @@ import com.fongmi.android.tv.bean.Result;
import com.fongmi.android.tv.bean.Site;
import com.fongmi.android.tv.databinding.ActivityVodBinding;
import com.fongmi.android.tv.ui.base.BaseActivity;
import com.fongmi.android.tv.ui.fragment.VodFragment;
import com.fongmi.android.tv.ui.fragment.TypeFragment;
import com.fongmi.android.tv.ui.presenter.TypePresenter;
import com.fongmi.android.tv.utils.KeyUtil;
import com.fongmi.android.tv.utils.ResUtil;
@ -41,7 +41,6 @@ public class VodActivity extends BaseActivity implements TypePresenter.OnClickLi
private ActivityVodBinding mBinding;
private ArrayObjectAdapter mAdapter;
private PageAdapter mPageAdapter;
private boolean coolDown;
private View mOldView;
public static void start(Activity activity, Result result) {
@ -146,13 +145,8 @@ public class VodActivity extends BaseActivity implements TypePresenter.OnClickLi
mAdapter.notifyArrayItemRangeChanged(0, mAdapter.size());
}
private VodFragment getFragment() {
return (VodFragment) mPageAdapter.instantiateItem(mBinding.pager, mBinding.pager.getCurrentItem());
}
private void setCoolDown() {
App.post(() -> coolDown = false, 2000);
coolDown = true;
private TypeFragment getFragment() {
return (TypeFragment) mPageAdapter.instantiateItem(mBinding.pager, mBinding.pager.getCurrentItem());
}
@Override
@ -168,16 +162,15 @@ public class VodActivity extends BaseActivity implements TypePresenter.OnClickLi
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (KeyUtil.isMenuKey(event)) updateFilter((Class) mAdapter.get(mBinding.pager.getCurrentItem()));
if (KeyUtil.isBackKey(event) && event.isLongPress() && getFragment().goRoot()) setCoolDown();
return super.dispatchKeyEvent(event);
}
@Override
protected void onBackInvoked() {
Class item = (Class) mAdapter.get(mBinding.pager.getCurrentItem());
if (item.getFilter() != null && item.getFilter()) updateFilter(item);
if (item != null && item.getFilter() != null && item.getFilter()) updateFilter(item);
else if (getFragment().canBack()) getFragment().goBack();
else if (!coolDown) super.onBackInvoked();
else super.onBackInvoked();
}
class PageAdapter extends FragmentStatePagerAdapter {
@ -190,7 +183,7 @@ public class VodActivity extends BaseActivity implements TypePresenter.OnClickLi
@Override
public Fragment getItem(int position) {
Class type = (Class) mAdapter.get(position);
return VodFragment.newInstance(getKey(), type.getTypeId(), type.getStyle(), type.getExtend(false), "1".equals(type.getTypeFlag()));
return TypeFragment.newInstance(getKey(), type.getTypeId(), type.getStyle(), type.getExtend(false), "1".equals(type.getTypeFlag()));
}
@Override

@ -14,8 +14,6 @@ public abstract class BaseFragment extends Fragment {
protected abstract ViewBinding getBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container);
private boolean init;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
@ -25,29 +23,12 @@ public abstract class BaseFragment extends Fragment {
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
initView();
initEvent();
}
protected void initView() {
}
protected void initData() {
}
private void onVisible() {
if (init) return;
initData();
init = true;
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) if (isResumed()) onVisible();
}
@Override
public void onResume() {
super.onResume();
if (getUserVisibleHint()) onVisible();
protected void initEvent() {
}
}

@ -64,6 +64,11 @@ public class CustomVerticalGridView extends VerticalGridView {
if (views != null) for (View view : views) view.setVisibility(View.VISIBLE);
}
public boolean isHeaderVisible() {
if (views != null) for (View view : views) if (view.getId() == R.id.recycler && view.getVisibility() == View.VISIBLE) return true;
return false;
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getAction() != KeyEvent.ACTION_DOWN) return super.dispatchKeyEvent(event);

@ -69,6 +69,7 @@ public class CollectFragment extends BaseFragment implements CustomScroller.Call
protected void initView() {
setRecyclerView();
setViewModel();
addVideo(mCollect);
}
private void setRecyclerView() {
@ -88,13 +89,8 @@ public class CollectFragment extends BaseFragment implements CustomScroller.Call
});
}
@Override
protected void initData() {
if (mCollect != null) addVideo(mCollect.getList());
}
private boolean checkLastSize(List<Vod> items) {
if (mLast == null || items.size() == 0) return false;
if (mLast == null || items.isEmpty()) return false;
int size = Product.getColumn() - mLast.size();
if (size == 0) return false;
size = Math.min(size, items.size());
@ -103,8 +99,12 @@ public class CollectFragment extends BaseFragment implements CustomScroller.Call
return true;
}
private void addVideo(Collect collect) {
if (collect != null) addVideo(collect.getList());
}
public void addVideo(List<Vod> items) {
if (checkLastSize(items) || requireActivity() == null || requireActivity().isFinishing()) return;
if (checkLastSize(items) || getActivity() == null || requireActivity().isFinishing()) return;
List<ListRow> rows = new ArrayList<>();
for (List<Vod> part : Lists.partition(items, Product.getColumn())) {
mLast = new ArrayObjectAdapter(new VodPresenter(this));
@ -132,10 +132,4 @@ public class CollectFragment extends BaseFragment implements CustomScroller.Call
mViewModel.searchContent(mCollect.getSite(), getKeyword(), page);
mScroller.setLoading(true);
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (mBinding != null && !isVisibleToUser) mBinding.recycler.moveToTop();
}
}

@ -0,0 +1,90 @@
package com.fongmi.android.tv.ui.fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentTransaction;
import androidx.viewbinding.ViewBinding;
import com.fongmi.android.tv.R;
import com.fongmi.android.tv.bean.Style;
import com.fongmi.android.tv.databinding.FragmentFolderBinding;
import com.fongmi.android.tv.ui.base.BaseFragment;
import java.util.HashMap;
public class TypeFragment extends BaseFragment {
public static TypeFragment newInstance(String key, String typeId, Style style, HashMap<String, String> extend, boolean folder) {
Bundle args = new Bundle();
args.putString("key", key);
args.putString("typeId", typeId);
args.putBoolean("folder", folder);
args.putParcelable("style", style);
args.putSerializable("extend", extend);
TypeFragment fragment = new TypeFragment();
fragment.setArguments(args);
return fragment;
}
private String getKey() {
return getArguments().getString("key");
}
private String getTypeId() {
return getArguments().getString("typeId");
}
private boolean getFolder() {
return getArguments().getBoolean("folder");
}
private Style getStyle() {
return getArguments().getParcelable("style");
}
private HashMap<String, String> getExtend() {
return (HashMap<String, String>) getArguments().getSerializable("extend");
}
@Override
protected ViewBinding getBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) {
return FragmentFolderBinding.inflate(inflater, container, false);
}
@Override
protected void initView() {
getChildFragmentManager().beginTransaction().replace(R.id.container, VodFragment.newInstance(getKey(), getTypeId(), getStyle(), getExtend(), getFolder())).commit();
}
public void openFolder(String typeId) {
VodFragment next = VodFragment.newInstance(getKey(), typeId, getStyle(), getExtend(), getFolder());
VodFragment current = (VodFragment) getChildFragmentManager().findFragmentById(R.id.container);
FragmentTransaction ft = getChildFragmentManager().beginTransaction();
if (current != null) ft.hide(current);
ft.add(R.id.container, next);
ft.addToBackStack(null);
ft.commit();
}
public void toggleFilter(boolean visible) {
VodFragment current = (VodFragment) getChildFragmentManager().findFragmentById(R.id.container);
if (current != null) current.toggleFilter(visible);
}
public void onRefresh() {
VodFragment current = (VodFragment) getChildFragmentManager().findFragmentById(R.id.container);
if (current != null) current.onRefresh();
}
public boolean canBack() {
return getChildFragmentManager().getBackStackEntryCount() > 0;
}
public void goBack() {
getChildFragmentManager().popBackStack();
}
}

@ -3,7 +3,6 @@ package com.fongmi.android.tv.ui.fragment;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
@ -21,7 +20,6 @@ import com.fongmi.android.tv.Product;
import com.fongmi.android.tv.R;
import com.fongmi.android.tv.api.config.VodConfig;
import com.fongmi.android.tv.bean.Filter;
import com.fongmi.android.tv.bean.Page;
import com.fongmi.android.tv.bean.Result;
import com.fongmi.android.tv.bean.Site;
import com.fongmi.android.tv.bean.Style;
@ -42,7 +40,6 @@ import com.fongmi.android.tv.utils.ResUtil;
import com.github.catvod.utils.Prefers;
import com.google.common.collect.Lists;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -56,9 +53,8 @@ public class VodFragment extends BaseFragment implements CustomScroller.Callback
private CustomScroller mScroller;
private SiteViewModel mViewModel;
private List<Filter> mFilters;
private List<Page> mPages;
private boolean mOpen;
private Page mPage;
private boolean headerVisible;
private boolean filterVisible;
public static VodFragment newInstance(String key, String typeId, Style style, HashMap<String, String> extend, boolean folder) {
Bundle args = new Bundle();
@ -77,32 +73,31 @@ public class VodFragment extends BaseFragment implements CustomScroller.Callback
}
private String getTypeId() {
return mPages.isEmpty() ? getArguments().getString("typeId") : getLastPage().getVodId();
}
private List<Filter> getFilter() {
return Filter.arrayFrom(Prefers.getString("filter_" + getKey() + "_" + getTypeId()));
return getArguments().getString("typeId");
}
private HashMap<String, String> getExtend() {
Serializable extend = getArguments().getSerializable("extend");
return extend == null ? new HashMap<>() : (HashMap<String, String>) extend;
return (HashMap<String, String>) getArguments().getSerializable("extend");
}
private boolean isFolder() {
return getArguments().getBoolean("folder");
}
private Style getStyle() {
return isFolder() ? Style.list() : getSite().getStyle(getArguments().getParcelable("style"));
}
private Site getSite() {
return VodConfig.get().getSite(getKey());
}
private Page getLastPage() {
return mPages.get(mPages.size() - 1);
private List<Filter> getFilter() {
return Filter.arrayFrom(Prefers.getString("filter_" + getKey() + "_" + getTypeId()));
}
private Style getStyle() {
return isFolder() ? Style.list() : getSite().getStyle(mPages.isEmpty() ? getArguments().getParcelable("style") : getLastPage().getStyle());
private TypeFragment getParent() {
return ((TypeFragment) getParentFragment());
}
@Override
@ -112,16 +107,11 @@ public class VodFragment extends BaseFragment implements CustomScroller.Callback
@Override
protected void initView() {
mPages = new ArrayList<>();
mExtends = getExtend();
mFilters = getFilter();
setRecyclerView();
setViewModel();
setFilters();
}
@Override
protected void initData() {
getVideo();
}
@ -143,11 +133,10 @@ public class VodFragment extends BaseFragment implements CustomScroller.Callback
mViewModel.result.observe(getViewLifecycleOwner(), result -> {
boolean first = mScroller.first();
int size = result.getList().size();
mBinding.progressLayout.showContent(first, size);
if (size > 0) addVideo(result);
mScroller.endLoading(result);
checkPosition(first);
checkMore(size);
hideProgress();
});
}
@ -176,7 +165,7 @@ public class VodFragment extends BaseFragment implements CustomScroller.Callback
boolean first = "1".equals(page);
if (first) mLast = null;
if (first) showProgress();
int filterSize = mOpen ? mFilters.size() : 0;
int filterSize = filterVisible ? mFilters.size() : 0;
boolean clear = first && mAdapter.size() > filterSize;
if (clear) mAdapter.removeItems(filterSize, mAdapter.size() - filterSize);
mViewModel.categoryContent(getKey(), typeId, page, true, mExtends);
@ -188,14 +177,6 @@ public class VodFragment extends BaseFragment implements CustomScroller.Callback
else addGrid(result.getList(), style);
}
private void checkPosition(boolean first) {
if (mPage != null && mPage.getPosition() > 0) mBinding.recycler.hideHeader();
if (mPage != null && mPage.getPosition() < 1) mBinding.recycler.showHeader();
if (mPage != null) mBinding.recycler.setSelectedPosition(mPage.getPosition());
else if (first && !mOpen) mBinding.recycler.moveToTop();
mPage = null;
}
private void checkMore(int count) {
if (mScroller.isDisable() || count == 0 || mAdapter.size() >= 5) return;
getVideo(getTypeId(), String.valueOf(mScroller.addPage()));
@ -231,11 +212,7 @@ public class VodFragment extends BaseFragment implements CustomScroller.Callback
}
private void showProgress() {
if (!mOpen) mBinding.progress.getRoot().setVisibility(View.VISIBLE);
}
private void hideProgress() {
mBinding.progress.getRoot().setVisibility(View.GONE);
if (!filterVisible) mBinding.progressLayout.showProgress();
}
private void showFilter() {
@ -243,48 +220,29 @@ public class VodFragment extends BaseFragment implements CustomScroller.Callback
for (Filter filter : mFilters) rows.add(getRow(filter));
App.post(() -> mBinding.recycler.scrollToPosition(0), 48);
mAdapter.addAll(0, rows);
hideProgress();
}
private void hideFilter() {
mAdapter.removeItems(0, mFilters.size());
}
public void toggleFilter(boolean open) {
if (open) showFilter();
public void toggleFilter(boolean visible) {
this.filterVisible = visible;
if (visible) showFilter();
else hideFilter();
mOpen = open;
}
public void onRefresh() {
getVideo();
}
public boolean canBack() {
return !mPages.isEmpty();
}
public void goBack() {
if (mPages.size() == 1) mBinding.recycler.setMoveTop(true);
mPages.remove(mPage = getLastPage());
onRefresh();
}
public boolean goRoot() {
if (mPages.isEmpty()) return false;
mPages.clear();
getVideo();
return true;
}
@Override
public void onItemClick(Vod item) {
if (item.isAction()) {
mViewModel.action(getKey(), item.getAction());
} else if (item.isFolder()) {
mPages.add(Page.get(item, mBinding.recycler.getSelectedPosition()));
mBinding.recycler.setMoveTop(false);
getVideo(item.getVodId(), "1");
getParent().openFolder(item.getVodId());
headerVisible = mBinding.recycler.isHeaderVisible();
} else {
if (getSite().isIndex()) CollectActivity.start(requireActivity(), item.getVodName());
else VideoActivity.start(requireActivity(), getKey(), item.getVodId(), item.getVodName(), item.getVodPic(), isFolder() ? item.getVodName() : null);
@ -303,9 +261,16 @@ public class VodFragment extends BaseFragment implements CustomScroller.Callback
getVideo(getTypeId(), page);
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (mBinding != null && !isVisibleToUser) mBinding.recycler.moveToTop();
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
if (hidden) {
mBinding.recycler.showHeader();
} else {
if (headerVisible) mBinding.recycler.showHeader();
else mBinding.recycler.hideHeader();
mBinding.recycler.requestFocus();
}
}
}

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
<com.fongmi.android.tv.ui.custom.ProgressLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/progressLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -17,9 +18,4 @@
app:focusOutEnd="true"
app:focusOutFront="true" />
<include
android:id="@+id/progress"
layout="@layout/view_progress"
android:visibility="gone" />
</FrameLayout>
</com.fongmi.android.tv.ui.custom.ProgressLayout>
Loading…
Cancel
Save