电视TV端使用RecyclerView开发遇到的问题

最近在做一个TV端使用的相册。

偷偷展示一下,相册大概长这个样子:
相册界面

相册也支持多选删除:
相册多选删除界面

非删除模式下点击单一图片会进入单一图片展示界面。单一图片可以左右翻页。
单一图片展示

从展示界面返回相册界面,光标定位到展示图片所在的相册位置。

主要控件使用了CardView 和RecyclerView。
TV开发和手机开发有个不同的就是焦点问题
在手机端,手指只要一划就可以到后面了,而在TV端,需要用遥控器左右键控制焦点移动。

RecyclerView(简称RV)在TV端的应用可以借鉴的案例不多,相比于ListView,RV的很多功能都需要自己实现。诸如,wrap_content自适应问题,选中的背景问题,setSelection()方法等。

将遇到的问题及大致解决方法mark一下,方便以后查询。


1.wrap_content自适应问题

参见RecyclerView自适应高度的LayoutManager

2.RV占据焦点,RV的子View没有获取焦点(ps:工程使用的是androidannotations框架,以下代码使用了androidannotations注解)

需要在RV获取焦点后手动设置下焦点:

@FocusChange(R.id.id_recyclerview_horizontal)
void focusRecyclerView(boolean hasFocus) {
        if (hasFocus) {
            focusChild(false);
        }
    }

 @UiThread
 void focusChild(boolean isInit) {
        if (mRecyclerView.getChildCount() > 0) {
            mRecyclerView.getChildAt(0).requestFocus();
        }
        if (isInit) {
            mAct.setSelectPos(0);
        }
    }

3.多选问题
多选问题一开始是使用的List保存需要删除的图片位置,在onBindViewHolder方法中根据list中的数据来做判断,决定显示垃圾筐(选择状态)还是正常图片(非选择状态)。这样做也能达到效果,但由于Item的View是一个包含多个控件的FrameLayout,这样写需要getChildAt或者findViewById来决定取舍,觉得这样有点low。
最后选择了com.bignerdranch.android.multiselector工具
嗯,好用。

4.选中的背景问题
在SwappingHolder的子类中复写如下方法:

 @Override
public void setSelectionModeBackgroundDrawable(Drawable selectionModeBackgroundDrawable) {  super.setSelectionModeBackgroundDrawable(mAct.getResources().getDrawable(R.drawable.selector_list_item_shadow));
}

5.setSelection()方法,跳转到最后几个Item的问题
RV没有setSelection方法,官方API是这么说的

RecyclerView will scroll the minimum amount that is necessary to make the target position visible. If you are looking for a similar behavior to setSelection(int) or setSelectionFromTop(int, int), use scrollToPositionWithOffset(int, int).
有个问题就是,如果总共有15张图片,最后一页展示4个图片,那么跳转到pos大于11的图片时候,焦点却在pos==11的图片位置上。所以这个时候需要手动设置下。

@UiThread
void fouceSelected() {
if (mAct.getSelectPos() 
<= linearLayoutManager.findLastVisibleItemPosition() &&
mAct.getSelectPos() >=linearLayoutManager.findFirstVisibleItemPosition()) {
            linearLayoutManager.findViewByPosition(mAct.getSelectPos()).requestFocus();

int focusPos = mRecyclerView.getChildCount() + mAct.getSelectPos() - linearLayoutManager.findLastVisibleItemPosition() - 1;
mRecyclerView.getChildAt(focusPos).requestFocus();
        }
    }

获取RV当前显示的第一个控件的位置和最后一个控件显示的位置来计算出当前应该获取焦点的View的位置,把焦点交给它。


  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
好的,我可以帮你解答这个问题。 首先,你需要在你的项目中添加 RecyclerView 的依赖项。可以在 `build.gradle` 文件中添加以下依赖项: ``` dependencies { implementation 'androidx.recyclerview:recyclerview:1.2.0' } ``` 接下来,你需要在你的布局文件中添加一个 RecyclerView 控件。例如: ``` <androidx.recyclerview.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" /> ``` 然后,你需要创建一个 RecyclerView 的 Adapter 类,并在里面实现数据绑定逻辑。例如: ``` public class SmsAdapter extends RecyclerView.Adapter<SmsAdapter.ViewHolder> { private List<Sms> smsList; public SmsAdapter(List<Sms> smsList) { this.smsList = smsList; } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_sms, parent, false); return new ViewHolder(view); } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { Sms sms = smsList.get(position); holder.tvAddress.setText(sms.getAddress()); holder.tvBody.setText(sms.getBody()); } @Override public int getItemCount() { return smsList.size(); } static class ViewHolder extends RecyclerView.ViewHolder { TextView tvAddress; TextView tvBody; ViewHolder(@NonNull View itemView) { super(itemView); tvAddress = itemView.findViewById(R.id.tv_address); tvBody = itemView.findViewById(R.id.tv_body); } } } ``` 在 Adapter 类中,我们定义了一个 ViewHolder 类来保存 RecyclerView 中的每个 item 的视图。在 onCreateViewHolder 方法中,我们创建 ViewHolder 对象并返回它。在 onBindViewHolder 方法中,我们将数据绑定到 ViewHolder 中的视图上。 接下来,你需要在你的 Activity 或 Fragment 类中实现 RecyclerView 的初始化逻辑。例如: ``` public class SmsActivity extends AppCompatActivity { private List<Sms> smsList = new ArrayList<>(); private SmsAdapter smsAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_sms); RecyclerView recyclerView = findViewById(R.id.recycler_view); recyclerView.setLayoutManager(new LinearLayoutManager(this)); smsAdapter = new SmsAdapter(smsList); recyclerView.setAdapter(smsAdapter); // 加载短信数据 loadSmsData(); } private void loadSmsData() { // TODO: 加载短信数据并添加到 smsList 中 // 省略具体实现 } } ``` 在这个例子中,我们在 onCreate 方法中初始化了 RecyclerView,设置了布局管理器和 Adapter,并在 loadSmsData 方法中加载短信数据并将其添加到 smsList 中。最后,我们需要调用 Adapter 的 notifyDataSetChanged 方法来刷新 RecyclerView 的内容。例如: ``` private void loadSmsData() { // 加载短信数据并添加到 smsList 中 smsList.addAll(getSmsList()); smsAdapter.notifyDataSetChanged(); } ``` 这样就可以在 RecyclerView 中显示短信内容了。当有新的短信到达时,你需要更新 smsList 数据并调用 Adapter 的 notifyDataSetChanged 方法来刷新 RecyclerView 的内容。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值