android TV常见需求,焦点item保持居中 —— RecyclerView自定义焦点滑动位置和滑动速度。

  android tv开发和移动端开发最大的不同就是多了一个焦点处理的逻辑。尤其是类似Recyclerview这样本身带有滑动效果,为了醒目的显示当前焦点在什么位置,需要滑动的时候回添加大量的动画、高亮、阴影等效果。  同样,让焦点位置不变而列表主动滑动也是一种常见的提醒焦点的手段。demo效果图如下,结尾放出全部代码:一、准备工作先导入recyclerviewdependencies { implementation 'androidx.recyclerview:recyclervi
摘要由CSDN通过智能技术生成

  android tv开发和移动端开发最大的不同就是多了一个焦点处理的逻辑。尤其是类似Recyclerview这样本身带有滑动效果,为了醒目的显示当前焦点在什么位置,需要滑动的时候回添加大量的动画、高亮、阴影等效果。
图片来自网络
  同样,让焦点位置不变而列表主动滑动也是一种常见的提醒焦点的手段。demo效果图如下,结尾放出全部代码
效果图

一、准备工作

先导入recyclerview

dependencies {
   
    implementation 'androidx.recyclerview:recyclerview:1.2.0-alpha03'
}

  我用的demo是androidx的recyclerview。低版本的同学可以使用android.support支持库。
  在布局文件中添加recyclerview的布局,并添加一个item的布局。findviewbyid找到recyclerview的控件,并setLayoutManager(我用的是LinearLayoutManager)和setAdapter。一个粗糙的recyclerview效果就出来了。这是最简单的recyclerview,除了能滑动,什么效果也没有。

二、突出焦点,添加放大动画和阴影

  允许item获得焦点,并为item设置焦点监听。这一步可以放到onBindViewHolder或者ViewHolder初始化的地方。
  为了能看出当前焦点的位置,还需要对获得焦点的item进行高亮处理。下面代码中,用setTranslationZ添加了阴影,ofFloatAnimator方法中还设置了放大动画。

		class MyHolder extends RecyclerView.ViewHolder{
   
            public MyHolder(@NonNull final View itemView) {
   
                super(itemView);
                itemView.setFocusable(true);
                itemView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
   
                    @Override
                    public void onFocusChange(View view, boolean b) {
   
                        if(b){
   
                            int[] amount = getScrollAmount(recyclerView, view);//计算需要滑动的距离
                            //滑动到指定距离
                            scrollToAmount(recyclerView, amount[0], amount[1]);

                            itemView.setTranslationZ(20);//阴影
                            ofFloatAnimator(itemView,1f,1.3f);//放大
                        }else {
   
                            itemView.setTranslationZ(0);
                            ofFloatAnimator(itemView,1.3f,1f);
                        }
                    }

                });
            }

//放大动画

        //放大动画
        private void ofFloatAnimator(View view,float start,float end){
   
            AnimatorSet animatorSet = new AnimatorSet();
            animatorSet.setDuration(700);//动画时间
            ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, "scaleX", start, end);
            ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, "scaleY", start, end);
            animatorSet.setInterpolator(new DecelerateInterpolator());//插值器
            animatorSet.play(scaleX).with(scaleY);//组合动画,同时基于x和y轴放大
            animatorSet.start();
        }

  因为item放大,体积超过了recyclerview的边界。为了使这部分正常显示,需要在布局文件中recyclerview的父布局添加clipChildren和clipToPadding属性。

    android:clipChildren="false"
    android:clipToPadding="false"

三、计算滑动距离,使焦点始终居中

  首先我们看看recyclerview源码是怎么控制滑动的距离的,什么时候需要滑动,什么时候不用滑动。在源码RecyclerView/的LayoutManager中有这样一段代码:

public boolean requestChildRectangleOnScreen(@NonNull RecyclerView parent,
                @NonNull View child, @NonNull Rect rect, boolean immediate,
                boolean focusedChildVisible) {
   
            int[] scrollAmount = getChildRectangleOnScreenScrollAmount(child, rect
            );
            int dx = scrollAmount[0];
            int dy = scrollAmount[1];
            if (!focusedChildVisible || isFocusedChildVisibleAfterScrolling(parent, dx, dy)) {
   
                if (dx != 0 || dy != 0) {
   
                    if (immediate) {
   
             
  • 7
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
实现 RecyclerView 绑定自定义的字母索引条位于控件的右侧并且竖直居中,可以按照以下步骤操作: 1. 在布局文件中,将 RecyclerView自定义的字母索引条控件放在同一个父布局中,如 LinearLayout。 2. 将 LinearLayout 的 `android:orientation` 属性设置为 `horizontal`,表示子控件应该水平排列。 3. 将 RecyclerView 的 `android:layout_weight` 属性设置为 1,表示在 LinearLayout 中占据剩余的空间。 4. 将字母索引条控件的 `android:layout_gravity` 属性设置为 `center_vertical|end`,表示在 LinearLayout 中垂直居中对齐,并且位于 LinearLayout 的右侧。 5. 在 RecyclerView 的适配器中实现 `getItemViewType` 方法,根据 item 的类型返回不同的 viewType。这个 viewType 就是 RecyclerView 中的一个 item 类型,需要在 onCreateViewHolder 方法中创建不同的 ViewHolder。 6. 在 RecyclerView 的适配器中实现 `onBindViewHolder` 方法,根据 viewType 绑定不同类型的 ViewHolder。 7. 在 RecyclerView 的适配器中实现自定义的字母索引条的数据源,并在 onBindViewHolder 方法中根据 item 数据的首字母设置字母索引条的可见性和文本内容。 下面是一个示例代码: ``` <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1"/> <TextView android:id="@+id/index_bar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical|end" android:textColor="#333333" android:textSize="16sp" android:padding="8dp" android:background="#DDDDDD" android:visibility="gone"/> </LinearLayout> ``` 适配器代码: ``` class MyAdapter(private val data: List<MyData>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { companion object { private const val TYPE_NORMAL = 0 private const val TYPE_HEADER = 1 } override fun getItemViewType(position: Int): Int { return if (position == 0) { TYPE_HEADER } else { TYPE_NORMAL } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return if (viewType == TYPE_HEADER) { HeaderViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_header, parent, false)) } else { NormalViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_normal, parent, false)) } } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { if (getItemViewType(position) == TYPE_HEADER) { val headerViewHolder = holder as HeaderViewHolder headerViewHolder.bind(data) } else { val normalViewHolder = holder as NormalViewHolder val item = data[position - 1] normalViewHolder.bind(item) } } override fun getItemCount(): Int { return data.size + 1 } class NormalViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { fun bind(item: MyData) { // 绑定普通 item 的数据 } } class HeaderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { private val indexBar = itemView.findViewById<TextView>(R.id.index_bar) fun bind(data: List<MyData>) { // 绑定 header 的数据 // 根据数据源设置 indexBar 的可见性和文本内容 // indexBar.visibility = View.VISIBLE // indexBar.text = "A B C D ..." } } } ``` 注意,这只是一个示例代码,具体实现可能因为你的布局结构和数据源不同而有所不同。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值