From 89ca49302b7e51a6237533b8b8325b8cfd027bef Mon Sep 17 00:00:00 2001 From: FongMi Date: Fri, 23 Sep 2022 09:04:42 +0800 Subject: [PATCH] [mobile] add vod fragment --- .../com/fongmi/android/tv/bean/Class.java | 9 ++ .../com/fongmi/android/tv/utils/ImgUtil.java | 1 + .../android/tv/ui/adapter/TypeAdapter.java | 92 +++++++++++++ .../android/tv/ui/adapter/VodAdapter.java | 3 +- .../android/tv/ui/fragment/ChildFragment.java | 127 ++++++++++++++++++ .../android/tv/ui/fragment/HomeFragment.java | 5 + .../android/tv/ui/fragment/VodFragment.java | 81 +++++++++++ .../res/drawable/selector_item_round.xml | 5 + .../drawable/shape_item_round_activated.xml | 15 +++ .../res/drawable/shape_item_round_normal.xml | 15 +++ app/src/mobile/res/layout/adapter_type.xml | 12 ++ app/src/mobile/res/layout/fragment_child.xml | 18 +++ app/src/mobile/res/layout/fragment_home.xml | 23 ++-- app/src/mobile/res/layout/fragment_vod.xml | 35 ++++- app/src/mobile/res/values-zh-rCN/strings.xml | 4 + app/src/mobile/res/values-zh-rTW/strings.xml | 4 + app/src/mobile/res/values/strings.xml | 4 + 17 files changed, 441 insertions(+), 12 deletions(-) create mode 100644 app/src/mobile/java/com/fongmi/android/tv/ui/adapter/TypeAdapter.java create mode 100644 app/src/mobile/java/com/fongmi/android/tv/ui/fragment/ChildFragment.java create mode 100644 app/src/mobile/res/drawable/selector_item_round.xml create mode 100644 app/src/mobile/res/drawable/shape_item_round_activated.xml create mode 100644 app/src/mobile/res/drawable/shape_item_round_normal.xml create mode 100644 app/src/mobile/res/layout/adapter_type.xml create mode 100644 app/src/mobile/res/layout/fragment_child.xml diff --git a/app/src/main/java/com/fongmi/android/tv/bean/Class.java b/app/src/main/java/com/fongmi/android/tv/bean/Class.java index cf9c52da2..9b1d1c193 100644 --- a/app/src/main/java/com/fongmi/android/tv/bean/Class.java +++ b/app/src/main/java/com/fongmi/android/tv/bean/Class.java @@ -24,6 +24,7 @@ public class Class { private String typeFlag; private Boolean filter; + private boolean activated; public String getTypeId() { return typeId; @@ -45,6 +46,14 @@ public class Class { return filter; } + public boolean isActivated() { + return activated; + } + + public void setActivated(boolean activated) { + this.activated = activated; + } + public Class toggleFilter() { setFilter(!getFilter()); return this; diff --git a/app/src/main/java/com/fongmi/android/tv/utils/ImgUtil.java b/app/src/main/java/com/fongmi/android/tv/utils/ImgUtil.java index 018b7eb5a..f3bd71e8c 100644 --- a/app/src/main/java/com/fongmi/android/tv/utils/ImgUtil.java +++ b/app/src/main/java/com/fongmi/android/tv/utils/ImgUtil.java @@ -18,6 +18,7 @@ import com.fongmi.android.tv.R; public class ImgUtil { public static void load(String vodPic, ImageView view) { + view.setScaleType(ImageView.ScaleType.CENTER); if (TextUtils.isEmpty(vodPic)) view.setImageResource(R.drawable.ic_img_error); else Glide.with(App.get()).asBitmap().load(vodPic).skipMemoryCache(true).sizeMultiplier(Prefers.getThumbnail()).signature(new ObjectKey(vodPic + "_" + Prefers.getQuality())).placeholder(R.drawable.ic_img_loading).listener(getListener(view)).into(view); } diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/TypeAdapter.java b/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/TypeAdapter.java new file mode 100644 index 000000000..8fb9a3710 --- /dev/null +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/TypeAdapter.java @@ -0,0 +1,92 @@ +package com.fongmi.android.tv.ui.adapter; + +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.fongmi.android.tv.bean.Class; +import com.fongmi.android.tv.databinding.AdapterTypeBinding; +import com.fongmi.android.tv.utils.ResUtil; + +import java.util.ArrayList; +import java.util.List; + +public class TypeAdapter extends RecyclerView.Adapter { + + private OnClickListener mListener; + private final List mItems; + + public TypeAdapter() { + this.mItems = new ArrayList<>(); + } + + public void setListener(OnClickListener listener) { + this.mListener = listener; + } + + public interface OnClickListener { + + void onItemClick(Class item); + } + + static class ViewHolder extends RecyclerView.ViewHolder { + + private final AdapterTypeBinding binding; + + ViewHolder(@NonNull AdapterTypeBinding binding) { + super(binding.getRoot()); + this.binding = binding; + } + } + + public void addAll(List items) { + if (items.isEmpty()) return; + mItems.clear(); + mItems.addAll(items); + mItems.get(0).setActivated(true); + notifyDataSetChanged(); + } + + public int setActivated(Class item) { + int position = mItems.indexOf(item); + setActivated(position); + return position; + } + + public void setActivated(int position) { + for (Class item : mItems) item.setActivated(false); + mItems.get(position).setActivated(true); + notifyItemRangeChanged(0, mItems.size()); + } + + public List getTypes() { + return mItems; + } + + public Class get(int position) { + return mItems.get(position); + } + + @Override + public int getItemCount() { + return mItems.size(); + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(AdapterTypeBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + Class item = mItems.get(position); + holder.binding.text.setText(item.getTypeName()); + holder.binding.text.setActivated(item.isActivated()); + holder.binding.text.setCompoundDrawablePadding(ResUtil.dp2px(4)); + holder.binding.text.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, item.getIcon(), 0); + holder.binding.getRoot().setOnClickListener(v -> mListener.onItemClick(item)); + } +} \ No newline at end of file diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/VodAdapter.java b/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/VodAdapter.java index 322f1482d..5bf50a18b 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/VodAdapter.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/VodAdapter.java @@ -51,9 +51,8 @@ public class VodAdapter extends RecyclerView.Adapter { } public void addAll(List items) { - mItems.clear(); mItems.addAll(items); - notifyDataSetChanged(); + notifyItemRangeInserted(mItems.size(), items.size()); } @Override diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/ChildFragment.java b/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/ChildFragment.java new file mode 100644 index 000000000..803865009 --- /dev/null +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/ChildFragment.java @@ -0,0 +1,127 @@ +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.lifecycle.ViewModelProvider; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.viewbinding.ViewBinding; + +import com.fongmi.android.tv.bean.Filter; +import com.fongmi.android.tv.bean.Vod; +import com.fongmi.android.tv.databinding.FragmentChildBinding; +import com.fongmi.android.tv.model.SiteViewModel; +import com.fongmi.android.tv.ui.activity.BaseFragment; +import com.fongmi.android.tv.ui.adapter.VodAdapter; +import com.fongmi.android.tv.ui.custom.CustomScroller; +import com.fongmi.android.tv.ui.custom.SpaceItemDecoration; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class ChildFragment extends BaseFragment implements CustomScroller.Callback, VodAdapter.OnClickListener { + + private FragmentChildBinding mBinding; + private HashMap mExtend; + private SiteViewModel mViewModel; + private CustomScroller mScroller; + private List mFilters; + private List mTypeIds; + private VodAdapter mAdapter; + + private String getTypeId() { + return getArguments().getString("typeId"); + } + + private String getFilter() { + return getArguments().getString("filter"); + } + + private boolean isFolder() { + return getArguments().getBoolean("folder"); + } + + public static ChildFragment newInstance(String typeId, String filter, boolean folder) { + Bundle args = new Bundle(); + args.putString("typeId", typeId); + args.putString("filter", filter); + args.putBoolean("folder", folder); + ChildFragment fragment = new ChildFragment(); + fragment.setArguments(args); + return fragment; + } + + @Override + protected ViewBinding getBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { + return mBinding = FragmentChildBinding.inflate(inflater, container, false); + } + + @Override + protected void initView() { + mTypeIds = new ArrayList<>(); + mExtend = new HashMap<>(); + mFilters = Filter.arrayFrom(getFilter()); + mBinding.progressLayout.showProgress(); + setRecyclerView(); + setViewModel(); + getVideo(); + } + + private void setRecyclerView() { + mBinding.recycler.setHasFixedSize(true); + mBinding.recycler.setAdapter(mAdapter = new VodAdapter(this)); + mBinding.recycler.addOnScrollListener(mScroller = new CustomScroller(this)); + mBinding.recycler.setLayoutManager(new GridLayoutManager(getContext(), 3)); + mBinding.recycler.addItemDecoration(new SpaceItemDecoration(3, 16)); + } + + private void setViewModel() { + mViewModel = new ViewModelProvider(this).get(SiteViewModel.class); + mViewModel.result.observe(getViewLifecycleOwner(), result -> { + mScroller.endLoading(result.getList().isEmpty()); + mBinding.progressLayout.showContent(); + mAdapter.addAll(result.getList()); + checkPage(); + }); + } + + private void getVideo() { + mScroller.reset(); + getVideo(getTypeId(), "1"); + } + + private void checkPage() { + if (mScroller.getPage() != 1 || mAdapter.getItemCount() >= 4 || isFolder()) return; + mScroller.addPage(); + getVideo(getTypeId(), "2"); + } + + private void getVideo(String typeId, String page) { + if (isFolder()) mTypeIds.add(typeId); + if (isFolder()) mBinding.recycler.scrollToPosition(0); + boolean clear = page.equals("1") && mAdapter.getItemCount() > mFilters.size(); + //if (clear) mAdapter.removeItems(mFilters.size(), mAdapter.size() - mFilters.size()); + mViewModel.categoryContent(typeId, page, true, mExtend); + } + + @Override + public void onLoadMore(String page) { + if (isFolder()) return; + mScroller.setLoading(true); + getVideo(getTypeId(), page); + } + + @Override + public void onItemClick(Vod item) { + + } + + @Override + public boolean onLongClick(Vod item) { + return false; + } +} diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/HomeFragment.java b/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/HomeFragment.java index 001a3710a..139a9885f 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/HomeFragment.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/HomeFragment.java @@ -19,6 +19,8 @@ import com.fongmi.android.tv.ui.adapter.HistoryAdapter; import com.fongmi.android.tv.ui.adapter.VodAdapter; import com.fongmi.android.tv.ui.custom.SpaceItemDecoration; +import org.greenrobot.eventbus.EventBus; + public class HomeFragment extends BaseFragment implements VodAdapter.OnClickListener, HistoryAdapter.OnClickListener { private FragmentHomeBinding mBinding; @@ -37,6 +39,7 @@ public class HomeFragment extends BaseFragment implements VodAdapter.OnClickList @Override protected void initView() { + mBinding.progressLayout.showProgress(); setRecyclerView(); setViewModel(); getHistory(); @@ -63,6 +66,8 @@ public class HomeFragment extends BaseFragment implements VodAdapter.OnClickList mViewModel = new ViewModelProvider(this).get(SiteViewModel.class); mViewModel.result.observe(getViewLifecycleOwner(), result -> { if (result != null) mVodAdapter.addAll(result.getList()); + mBinding.progressLayout.showContent(); + EventBus.getDefault().post(result); }); } diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/VodFragment.java b/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/VodFragment.java index 9378dbddb..53014bdf1 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/VodFragment.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/VodFragment.java @@ -5,14 +5,32 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentStatePagerAdapter; import androidx.viewbinding.ViewBinding; +import androidx.viewpager.widget.ViewPager; +import com.fongmi.android.tv.api.ApiConfig; +import com.fongmi.android.tv.bean.Class; +import com.fongmi.android.tv.bean.Result; import com.fongmi.android.tv.databinding.FragmentVodBinding; import com.fongmi.android.tv.ui.activity.BaseFragment; +import com.fongmi.android.tv.ui.adapter.TypeAdapter; +import com.google.gson.Gson; + +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; + +import java.util.ArrayList; +import java.util.List; public class VodFragment extends BaseFragment { private FragmentVodBinding mBinding; + private TypeAdapter mAdapter; + private Result mResult; public static VodFragment newInstance() { return new VodFragment(); @@ -22,4 +40,67 @@ public class VodFragment extends BaseFragment { protected ViewBinding getBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { return mBinding = FragmentVodBinding.inflate(inflater, container, false); } + + @Override + protected void initView() { + mBinding.recycler.setHasFixedSize(true); + mBinding.recycler.setAdapter(mAdapter = new TypeAdapter()); + mBinding.pager.setAdapter(new PageAdapter(getChildFragmentManager())); + } + + @Override + protected void initEvent() { + EventBus.getDefault().register(this); + mAdapter.setListener(item -> mBinding.pager.setCurrentItem(mAdapter.setActivated(item))); + mBinding.pager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { + @Override + public void onPageSelected(int position) { + mBinding.recycler.smoothScrollToPosition(position); + mAdapter.setActivated(position); + } + }); + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onResult(Result result) { + setAdapter(mResult = result); + } + + private void setAdapter(Result result) { + result.setTypes(getTypes()); + mAdapter.addAll(result.getTypes()); + Boolean filter = ApiConfig.get().getHome().isFilterable() ? false : null; + for (Class item : mAdapter.getTypes()) if (result.getFilters().containsKey(item.getTypeId())) item.setFilter(filter); + mBinding.pager.getAdapter().notifyDataSetChanged(); + } + + private List getTypes() { + List types = new ArrayList<>(); + for (String cate : ApiConfig.get().getHome().getCategories()) for (Class item : mResult.getTypes()) if (cate.equals(item.getTypeName())) types.add(item); + return types; + } + + class PageAdapter extends FragmentStatePagerAdapter { + + public PageAdapter(@NonNull FragmentManager fm) { + super(fm); + } + + @NonNull + @Override + public Fragment getItem(int position) { + Class type = mAdapter.get(position); + String filter = new Gson().toJson(mResult.getFilters().get(type.getTypeId())); + return ChildFragment.newInstance(type.getTypeId(), filter, type.getTypeFlag().equals("1")); + } + + @Override + public int getCount() { + return mAdapter.getItemCount(); + } + + @Override + public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { + } + } } diff --git a/app/src/mobile/res/drawable/selector_item_round.xml b/app/src/mobile/res/drawable/selector_item_round.xml new file mode 100644 index 000000000..b345ec0a2 --- /dev/null +++ b/app/src/mobile/res/drawable/selector_item_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/mobile/res/drawable/shape_item_round_activated.xml b/app/src/mobile/res/drawable/shape_item_round_activated.xml new file mode 100644 index 000000000..65f476d67 --- /dev/null +++ b/app/src/mobile/res/drawable/shape_item_round_activated.xml @@ -0,0 +1,15 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/mobile/res/drawable/shape_item_round_normal.xml b/app/src/mobile/res/drawable/shape_item_round_normal.xml new file mode 100644 index 000000000..468adb022 --- /dev/null +++ b/app/src/mobile/res/drawable/shape_item_round_normal.xml @@ -0,0 +1,15 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/mobile/res/layout/adapter_type.xml b/app/src/mobile/res/layout/adapter_type.xml new file mode 100644 index 000000000..f9248b75c --- /dev/null +++ b/app/src/mobile/res/layout/adapter_type.xml @@ -0,0 +1,12 @@ + + \ No newline at end of file diff --git a/app/src/mobile/res/layout/fragment_child.xml b/app/src/mobile/res/layout/fragment_child.xml new file mode 100644 index 000000000..246fd5c86 --- /dev/null +++ b/app/src/mobile/res/layout/fragment_child.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/app/src/mobile/res/layout/fragment_home.xml b/app/src/mobile/res/layout/fragment_home.xml index 71e7e063a..fb32b8911 100644 --- a/app/src/mobile/res/layout/fragment_home.xml +++ b/app/src/mobile/res/layout/fragment_home.xml @@ -1,7 +1,8 @@ + android:layout_height="match_parent" + android:fillViewport="true"> @@ -36,16 +37,22 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" - android:text="更新推薦" + android:text="@string/home_recommend" android:textColor="@color/white" android:textSize="18sp" /> - + android:layout_height="match_parent" + android:layout_marginTop="16dp"> + + + \ No newline at end of file diff --git a/app/src/mobile/res/layout/fragment_vod.xml b/app/src/mobile/res/layout/fragment_vod.xml index 0f36c22be..afb7ed355 100644 --- a/app/src/mobile/res/layout/fragment_vod.xml +++ b/app/src/mobile/res/layout/fragment_vod.xml @@ -1,6 +1,37 @@ - - \ No newline at end of file + + + + + + + + + diff --git a/app/src/mobile/res/values-zh-rCN/strings.xml b/app/src/mobile/res/values-zh-rCN/strings.xml index a60b0bc27..86d53d46c 100644 --- a/app/src/mobile/res/values-zh-rCN/strings.xml +++ b/app/src/mobile/res/values-zh-rCN/strings.xml @@ -5,4 +5,8 @@ 片库 设定 + + 最近观看 + 更新推荐 + \ No newline at end of file diff --git a/app/src/mobile/res/values-zh-rTW/strings.xml b/app/src/mobile/res/values-zh-rTW/strings.xml index d8530e2c7..27b642788 100644 --- a/app/src/mobile/res/values-zh-rTW/strings.xml +++ b/app/src/mobile/res/values-zh-rTW/strings.xml @@ -5,4 +5,8 @@ 片庫 設定 + + 最近觀看 + 更新推薦 + \ No newline at end of file diff --git a/app/src/mobile/res/values/strings.xml b/app/src/mobile/res/values/strings.xml index 9b544160c..ed8ecb867 100644 --- a/app/src/mobile/res/values/strings.xml +++ b/app/src/mobile/res/values/strings.xml @@ -5,4 +5,8 @@ Vod Setting + + History + Recommend + \ No newline at end of file