Android TV 焦点控制实例

首先上实例:

Android TV 焦点控制实例

项目工程文件:

Android TV 焦点控制实例

 

 

 Android TV 焦点控制实例

 

界面布局:

activity_tab.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/fastlane_background"
    android:orientation="vertical"
    tools:context=".activity.TabActivity">


    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv_tan"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:descendantFocusability="afterDescendants"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintLeft_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="4"
        android:orientation="horizontal">

        <ImageView
            android:id="@+id/main_img"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="2"
            android:background="@drawable/bg_recyclerview_item"
            android:focusable="true"
            android:focusableInTouchMode="true"
            android:padding="15dp"
            android:src="@drawable/pro2"



            />

       <!-- <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/main_tab_rv"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="2"/>-->


        <com.example.focusdome.view.FocusRecyclerView
            android:id="@+id/main_tab_rv"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"

            />


    </LinearLayout>


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">

    <androidx.recyclerview.widget.RecyclerView

        android:id="@+id/button_tab_rv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"


        />
    </LinearLayout>

</LinearLayout>

layout_found_h_recycler_item.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:focusable="true"
    android:background="@drawable/bg_recyclerview_item"
    android:padding="20dp"
    >

    <com.example.focusdome.view.CircleImageView
        android:id="@+id/f_h_c_img"
        android:layout_width="109dp"
        android:layout_height="0dp"
        android:layout_gravity="center"
        android:layout_weight="3"

        android:src="@drawable/pro5"


        />


    <TextView
        android:id="@+id/f_h_c_txt"
        android:layout_width="120dp"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:gravity="center"
        android:text="1111"
        android:textSize="10dp"
        />





</LinearLayout>

layout_main_recycler_txtrecycler.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:focusable="true"
    android:background="@drawable/bg_recyclerview_item"
    >

    <TextView
        android:id="@+id/main_rv_rv_txt"
        android:layout_width="match_parent"
        android:layout_height="60dp"


        android:gravity="center"
        />


</LinearLayout>

layout_recycler_text_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:orientation="horizontal"
    android:gravity="center"
    android:background="@drawable/bg_recyclerview_item"
    android:focusable="true">


    <TextView
        android:id="@+id/tv"
        android:layout_width="120dp"
        android:layout_height="60dp"
        android:textColor="#ffffff"
        android:textSize="16sp"
        android:text=""
        android:padding="10dp"
        android:gravity="center"
        />

</LinearLayout>

资源文件:

bg_recyclerview_item.xml

 

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@drawable/bg_recyclerview_focus" android:state_selected="true" />
    <item android:drawable="@drawable/bg_recyclerview_focus" android:state_focused="true" />
    <item android:drawable="@drawable/bg_recyclerview_focus" android:state_pressed="true" />
    <item android:drawable="@color/transparent"/>
</selector>

bg_recyclerview_focus.xml:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape android:shape="rectangle">
            <solid android:color="#80B469FF" />
            <corners android:radius="10dp" />
        </shape>
    </item>
    <item android:bottom="2dp"
        android:left="2dp"
        android:right="2dp"
        android:top="2dp">
        <shape android:shape="rectangle">
            <solid android:color="#60B469FF" />
            <corners android:radius="8dp" />
        </shape>
    </item>
    <item
        android:bottom="5dp"
        android:left="5dp"
        android:right="5dp"
        android:top="5dp">
        <shape
            android:layout_width="wrap_content"
            android:shape="rectangle">
            <stroke
                android:width="1dp"
                android:color="#6672f8" />
        </shape>
    </item>
    <item
        android:bottom="6dp"
        android:left="6dp"
        android:right="6dp"
        android:top="6dp">
        <shape android:shape="rectangle">
            <stroke
                android:width="1dp"
                android:color="#6672f8" />
        </shape>
    </item>

    <item
        android:bottom="7dp"
        android:left="7dp"
        android:right="7dp"
        android:top="7dp">
        <color android:color="@color/color_141843"/>
    </item>
</layer-list>

activity代码:

package com.example.focusdome.activity;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.example.focusdome.R;
import com.example.focusdome.adapter.BtnRecyclerViewAdapter;
import com.example.focusdome.adapter.MainRecyclerViewAdapter;
import com.example.focusdome.adapter.MyRecyclerViewAdapter;
import com.example.focusdome.model.RecyclerMainModel;
import com.example.focusdome.view.FocusLinearLayoutManager;
import com.example.focusdome.view.SpaceDecoration;

import java.util.ArrayList;

public class TabActivity extends Activity implements MyRecyclerViewAdapter.OnItemClickListener,MyRecyclerViewAdapter.OnItemFocusChangeListener{

    private MyRecyclerViewAdapter adapter;
    private RecyclerView recyclerView;
    private String[] titles = {"首页", "游戏","教育","生活","娱乐","新闻","直播","我的"};

    private ImageView imageView;


    //主体部分
    private MainRecyclerViewAdapter mAdapter;
    private RecyclerView mainRecyclerView;

    private ArrayList<RecyclerMainModel> list = new ArrayList<>();



   //底部部分
    private BtnRecyclerViewAdapter btnRecyclerViewAdapter;
    private RecyclerView btnRecyclerView;




    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tab);
        initView();
        initAdapter();

        //初始化主体部分recycle
        mainRecyclerView = findViewById(R.id.main_tab_rv);

        list.add(new RecyclerMainModel("第一张",R.drawable.pre));
        list.add(new RecyclerMainModel("第二张",R.drawable.pro));
        list.add(new RecyclerMainModel("第三张",R.drawable.pro2));
        list.add(new RecyclerMainModel("第四张",R.drawable.pro3));
        list.add(new RecyclerMainModel("第五张",R.drawable.pro4));
        list.add(new RecyclerMainModel("第六张",R.drawable.hp_one));
        list.add(new RecyclerMainModel("第七张",R.drawable.hp_four));
        list.add(new RecyclerMainModel("第八张",R.drawable.hp_five));


        mAdapter = new MainRecyclerViewAdapter(this, list, new MainRecyclerViewAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                Log.v("点击:", "" + position + view.getId());

                imageView.setImageResource(list.get(position).getM_imgId());
                //imageView.setScaleType(ImageView.ScaleType.CENTER);


            }
        }/*, new MainRecyclerViewAdapter.OnItemFocusChangeListener() {
            @Override
            public void onItemFocusChange(View view, int position, boolean hasFocus) {


               // Toast.makeText(TabActivity.this,"focus"+position,Toast.LENGTH_LONG).show();

                imageView.setImageResource(list.get(position).getM_imgId());


            }
        }*/) {

            //获得焦点时处理
            @Override
            protected void onItemFocus(View itemView,int positon) {
               imageView.setImageResource(list.get(positon).getM_imgId());

               System.out.println("获取焦点成功!"+positon);
                Log.i("view:",itemView.getId()+"23333");
                itemView.setSelected(true);
                View view = itemView.findViewById(R.id.main_rv_rv_txt);

                view.setSelected(true);


            }
            //失去焦点时

            @Override
            protected void onItemGetNormal(View itemView) {
                System.out.println("失去焦点成功!");
                Log.i("view:",itemView.getId()+"32222");
               itemView.setSelected(false);
                View view = itemView.findViewById(R.id.main_rv_rv_txt);
                view.setSelected(true);


            }
        };
        FocusLinearLayoutManager linearLayoutManager = new FocusLinearLayoutManager(this,LinearLayoutManager.VERTICAL,false);
        mainRecyclerView.setLayoutManager(linearLayoutManager);
        mainRecyclerView.addItemDecoration(new SpaceDecoration(30));
        mainRecyclerView.setAdapter(mAdapter);



        //初始化底部部分
        btnRecyclerView = (RecyclerView)findViewById(R.id.button_tab_rv);
        btnRecyclerViewAdapter = new BtnRecyclerViewAdapter(this, list) {
            @Override
            protected void onItemFocus(View itemView, int position) {
                itemView.setSelected(true);
                //View view = itemView.
            }

            @Override
            protected void onItemGetNormal(View itemView) {
                   itemView.setSelected(false);
            }
        };

        FocusLinearLayoutManager linearLayoutManager1 = new FocusLinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false);

        btnRecyclerView.setLayoutManager(linearLayoutManager1);
        btnRecyclerView.setAdapter(btnRecyclerViewAdapter);




    }







    private void initView() {
        recyclerView = findViewById(R.id.rv_tan);
        imageView= findViewById(R.id.main_img);


        imageView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View view, boolean b) {
                Log.v("img_focus",""+view.getId());
            }
        });

    }

    private void initAdapter() {
        adapter = new MyRecyclerViewAdapter(this, titles);
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false);
        recyclerView.setLayoutManager(linearLayoutManager);
        recyclerView.setAdapter(adapter);
        recyclerView.requestFocus();
        adapter.setmOnItemClickListener(this);
        adapter.setmOnItemClickListener(this);
    }

    @Override
    public void onItemClick(View view, int position) {
        Log.i("Activity:onItemClick","mOnItemFocusChangeListener.onItemFocusChange(v, position, hasFocus);");
    }

    @Override
    public void onItemFocusChange(View view, int position, boolean hasFocus) {



    }


    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {

        


        return super.onKeyDown(keyCode, event);
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        //View v =  getCurrentFocus();




        return super.dispatchKeyEvent(event);



    }
}

adapter代码:

其余的adapter基本和这个差不多

package com.example.focusdome.adapter;

import android.content.Context;
import android.os.Build;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.example.focusdome.R;
import com.example.focusdome.activity.TabActivity;
import com.example.focusdome.model.RecyclerMainModel;

import java.util.ArrayList;

public abstract class BtnRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private Context context;

    private ArrayList<RecyclerMainModel> data;

    private MyRecyclerViewAdapter.OnItemFocusChangeListener mOnItemFocusChangeListener;

    private MyRecyclerViewAdapter.OnItemClickListener mOnItemClickListener;

    private final LayoutInflater layoutInflater;
    private int defaultFocus = 0;
    private boolean needFocus = true;


    public BtnRecyclerViewAdapter(Context context, ArrayList<RecyclerMainModel> list) {
        this.context = context;
        this.data = list;
        layoutInflater = LayoutInflater.from(context);
    }

    public interface OnItemClickListener{
        void onItemClick(View view, int position);

    }


    public interface OnItemFocusChangeListener{
        void onItemFocusChange(View view, int position, boolean hasFocus);

    }

    public final void setmOnItemClickListener(@Nullable MyRecyclerViewAdapter.OnItemClickListener listener){
        mOnItemClickListener = listener;
    }

    private void setmOnItemFocusChangeListener(@Nullable MyRecyclerViewAdapter.OnItemFocusChangeListener focusChangeListener){
        this.mOnItemFocusChangeListener = focusChangeListener;
    }



    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View v = layoutInflater.inflate(R.layout.layout_found_h_recycler_item,parent,false);


        return  new RecyclerViewHolder(v);
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder,final int position) {
        RecyclerViewHolder viewHolder = (RecyclerViewHolder)holder;

        viewHolder.imageView.setImageResource(data.get(position).getM_imgId());
        viewHolder.textView.setText(data.get(position).getM_txt());

        if (mOnItemClickListener != null) {
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    mOnItemClickListener.onItemClick(v, position);
                    Log.e("click事件:","你点击了"+v.getId());
                }
            });
        }


            holder.itemView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
                @Override
                public void onFocusChange(View v, boolean hasFocus) {
                  if (hasFocus){
                      focusStatus(v,position);


                  }else {

                      normalStatus(v);

                  }

                    Log.i("adapter:","onFocusChange");
                }
            });




        if (needFocus) {
            Log.i("进行移动:",":");

            if (defaultFocus < 0) {

                defaultFocus = 0;
            }
            if (defaultFocus >= getItemCount()) {
                defaultFocus = getItemCount() - 1;
            }
            if (defaultFocus == position) {
                if (!holder.itemView.isFocusable()) {
                    defaultFocus++;
                } else {
                    holder.itemView.requestFocus();
                }
            }
        } else {
            //setNeedFocus(position == (getItemCount() - 1));
        }

    }

    @Override
    public int getItemCount() {
        return data.size();
    }





    private class RecyclerViewHolder extends RecyclerView.ViewHolder {

        public TextView textView;

        public ImageView imageView;


        public RecyclerViewHolder(@NonNull View itemView) {
            super(itemView);

            textView = (TextView) itemView.findViewById(R.id.f_h_c_txt);

            imageView = (ImageView)itemView.findViewById(R.id.f_h_c_img);


        }
    }

    /**
     * item获得焦点时调用
     *
     * @param itemView view
     */
    private void focusStatus(View itemView,int position) {
        if (itemView == null) {
            return;
        }


        float scalX,scalY;
        scalX = 1.1f;
        scalY = 1.1f;
        if (Build.VERSION.SDK_INT >= 21) {
            //抬高Z轴
            ViewCompat.animate(itemView).scaleX(scalX).scaleY(scalY).translationZ(1.3f).start();
        } else {
            ViewCompat.animate(itemView).scaleX(scalX).scaleY(scalY).start();
            ViewGroup parent = (ViewGroup) itemView.getParent();
            parent.requestLayout();
            parent.invalidate();
        }
        onItemFocus(itemView,position);
    }

    /**
     * 当item获得焦点时处理
     *
     * @param itemView itemView
     */
    protected abstract void onItemFocus(View itemView,int position);

    /**
     * item失去焦点时
     *
     * @param itemView item对应的View
     */
    private void normalStatus(View itemView) {
        if (itemView == null) {
            return;
        }
        if (Build.VERSION.SDK_INT >= 21) {
            ViewCompat.animate(itemView).scaleX(1.0f).scaleY(1.0f).translationZ(0).start();
        } else {
            ViewCompat.animate(itemView).scaleX(1.0f).scaleY(1.0f).start();
            ViewGroup parent = (ViewGroup) itemView.getParent();
            parent.requestLayout();
            parent.invalidate();
        }
        onItemGetNormal(itemView);
    }

    /**
     * 当条目失去焦点时调用
     *
     * @param itemView 条目对应的View
     */
    protected abstract void onItemGetNormal(View itemView);


}

model代码:

package com.example.focusdome.model;

public class RecyclerMainModel {
    private String m_txt;
    private int m_imgId;

    public RecyclerMainModel(String m_txt, int m_imgId) {
        this.m_txt = m_txt;
        this.m_imgId = m_imgId;
    }

    public String getM_txt() {
        return m_txt;
    }

    public void setM_txt(String m_txt) {
        this.m_txt = m_txt;
    }

    public int getM_imgId() {
        return m_imgId;
    }

    public void setM_imgId(int m_imgId) {
        this.m_imgId = m_imgId;
    }
}

view代码:

CircleImageView.java:

自定义圆形ImageView

package com.example.focusdome.view;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;

import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;

@SuppressLint("AppCompatCustomView")
public class CircleImageView extends ImageView { //画笔
    private Paint mPaint;
    //圆形图片的半径
    private int mRadius;
    //图片的宿放比例
    private float mScale;

    public CircleImageView(Context context) {
        super(context);
    }

    public CircleImageView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public CircleImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //由于是圆形,宽高应保持一致
        int size = Math.min(getMeasuredWidth(), getMeasuredHeight());
        mRadius = size / 2;
        setMeasuredDimension(size, size);
    }

    @SuppressLint("DrawAllocation")
    @Override
    protected void onDraw(Canvas canvas) {

        mPaint = new Paint();
        mPaint.setColor(Color.WHITE);

        Drawable drawable = getDrawable();

        if (null != drawable) {
            Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();

            //初始化BitmapShader,传入bitmap对象
            BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
            //计算缩放比例
            mScale = (mRadius * 2.0f) / Math.min(bitmap.getHeight(), bitmap.getWidth());

            Matrix matrix = new Matrix();
            matrix.setScale(mScale, mScale);
            bitmapShader.setLocalMatrix(matrix);
            mPaint.setShader(bitmapShader);
            //画圆形,指定好坐标,半径,画笔
            canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);
        } else {
            super.onDraw(canvas);
        }
    }





}

自定义RecyclerView,重写dispatchKeyEvent()方法,进行焦点控制:

FocusRecyclerView:

package com.example.focusdome.view;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.FocusFinder;
import android.view.KeyEvent;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;

public class FocusRecyclerView extends RecyclerView {
    public FocusRecyclerView(@NonNull Context context) {
        super(context);
    }

    public FocusRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public FocusRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }


    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        if (event.getAction() == KeyEvent.ACTION_DOWN) {
            int keyCode = event.getKeyCode();

            //水平垂直的时候:按下左键或者右键
            if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT /*|| keyCode == KeyEvent.KEYCODE_DPAD_DOWN || keyCode == KeyEvent.KEYCODE_DPAD_UP*/) {

                //获取当前焦点的视图
                View focusedView = getFocusedChild();
                View nextFocusView;
                try {

                   /* switch (keyCode){
                        case KeyEvent.KEYCODE_DPAD_LEFT:
                            Log.v("left","往左");
                            //通过findNextFocus获取下一个需要的焦点的vie
                            nextFocusView = FocusFinder.getInstance().findNextFocus(this,focusedView,View.FOCUS_LEFT);
                            break;

                        case KeyEvent.KEYCODE_DPAD_RIGHT:
                            Log.v("right","往右");
                            nextFocusView = FocusFinder.getInstance().findNextFocus(this,focusedView,View.FOCUS_RIGHT);
                            break;

                        case KeyEvent.KEYCODE_DPAD_UP:
                            Log.v("up","往上");
                            nextFocusView = FocusFinder.getInstance().findNextFocus(this,focusedView,View.FOCUS_UP);
                            break;

                        case KeyEvent.KEYCODE_DPAD_DOWN:
                            nextFocusView = FocusFinder.getInstance().findNextFocus(this,focusedView,View.FOCUS_DOWN);

                            Log.v("down","往下");
                            break;
                        default:
                            nextFocusView = null;
                            break;


                    }*/


                    if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {

                        Log.v("left", "焦点往左跑");
                        //通过findNextFocus获取下一个需要得到的焦点的view
                        nextFocusView = FocusFinder.getInstance().findNextFocus(this, focusedView, View.FOCUS_LEFT);
                        if(nextFocusView == null){
                            Log.v("同级","View查找");
                          //  nextFocusView = FocusFinder.getInstance().findNextFocus(this,focusedView.getRootView().,View.FOCUS_LEFT);
                             nextFocusView = focusedView.getParent().focusSearch(focusedView,View.FOCUS_LEFT);

                        }

                    } else {
                        Log.v("right", "右边跑");
                        //通过findNextFocus获取下一个需要得到的焦点的View
                        nextFocusView = FocusFinder.getInstance().findNextFocus(this, focusedView, View.FOCUS_RIGHT);
                    }


                } catch (Exception e) {

                    nextFocusView = null;
                }
                //如果获取失败,消耗掉事件,不让系统处理,并让原先的view获取焦点
                if (nextFocusView == null) {


                    focusedView.requestFocus();
                    return true;
                }

            }


        }


        return super.dispatchKeyEvent(event);
    }


    @Override
    public View focusSearch(int direction) {


        return super.focusSearch(direction);
    }
}

SpaceDecoration.java

package com.example.focusdome.view;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.view.View;

import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.OrientationHelper;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;

import com.chad.library.adapter.base.BaseQuickAdapter;

import static android.widget.LinearLayout.VERTICAL;

public class SpaceDecoration extends RecyclerView.ItemDecoration {

    private int space;
    private int headerCount = -1;
    private int footerCount = Integer.MAX_VALUE;
    private boolean mPaddingEdgeSide = true;
    private boolean mPaddingStart = true;
    private boolean mPaddingHeaderFooter = false;
    private ColorDrawable mColorDrawable;

    private boolean mDrawLastItem = true;
    private boolean mDrawHeaderFooter = false;


    public SpaceDecoration(int space) {
        this.mColorDrawable = new ColorDrawable(Color.parseColor("#e7e7e7"));
        this.space = space;
    }

    public SpaceDecoration(int space, int color) {
        this.mColorDrawable = new ColorDrawable(color);
        this.space = space;
    }

    public void setPaddingEdgeSide(boolean mPaddingEdgeSide) {
        this.mPaddingEdgeSide = mPaddingEdgeSide;
    }

    public void setPaddingStart(boolean mPaddingStart) {
        this.mPaddingStart = mPaddingStart;
    }

    public void setPaddingHeaderFooter(boolean mPaddingHeaderFooter) {
        this.mPaddingHeaderFooter = mPaddingHeaderFooter;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        int position = parent.getChildAdapterPosition(view);
        int spanCount = 0;
        int orientation = 0;
        int spanIndex = 0;
        int headerCount = 0, footerCount = 0;
        if (parent.getAdapter() instanceof BaseQuickAdapter) {
            headerCount = ((BaseQuickAdapter) parent.getAdapter()).getHeaderLayoutCount();
            footerCount = ((BaseQuickAdapter) parent.getAdapter()).getFooterLayoutCount();
        }

        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof StaggeredGridLayoutManager) {
            orientation = ((StaggeredGridLayoutManager) layoutManager).getOrientation();
            spanCount = ((StaggeredGridLayoutManager) layoutManager).getSpanCount();
            spanIndex = ((StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams()).getSpanIndex();
        } else if (layoutManager instanceof GridLayoutManager) {
            orientation = ((GridLayoutManager) layoutManager).getOrientation();
            spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
            spanIndex = ((GridLayoutManager.LayoutParams) view.getLayoutParams()).getSpanIndex();
        } else if (layoutManager instanceof LinearLayoutManager) {
            orientation = ((LinearLayoutManager) layoutManager).getOrientation();
            spanCount = 1;
            spanIndex = 0;
        }

        /**
         * 普通Item的尺寸
         */
        if ((position >= headerCount && position < parent.getAdapter().getItemCount() - footerCount)) {

            if (orientation == VERTICAL) {
                float expectedWidth = (float) (parent.getWidth() - space * (spanCount + (mPaddingEdgeSide ? 1 : -1))) / spanCount;
                float originWidth = (float) parent.getWidth() / spanCount;
                float expectedX = (mPaddingEdgeSide ? space : 0) + (expectedWidth + space) * spanIndex;
                float originX = originWidth * spanIndex;
                outRect.left = (int) (expectedX - originX);
                outRect.right = (int) (originWidth - outRect.left - expectedWidth);
                if (position - headerCount < spanCount && mPaddingStart) {
                    outRect.top = space;
                }
                outRect.bottom = space;
                return;
            } else {
                float expectedHeight = (float) (parent.getHeight() - space * (spanCount + (mPaddingEdgeSide ? 1 : -1))) / spanCount;
                float originHeight = (float) parent.getHeight() / spanCount;
                float expectedY = (mPaddingEdgeSide ? space : 0) + (expectedHeight + space) * spanIndex;
                float originY = originHeight * spanIndex;
                outRect.bottom = (int) (expectedY - originY);
                outRect.top = (int) (originHeight - outRect.bottom - expectedHeight);
                if (position - headerCount < spanCount && mPaddingStart) {
                    outRect.left = space;
                }
                outRect.right = space;
                return;
            }
        } else if (mPaddingHeaderFooter) {
            if (orientation == VERTICAL) {
                outRect.right = outRect.left = mPaddingEdgeSide ? space : 0;
                outRect.top = mPaddingStart ? space : 0;
            } else {
                outRect.top = outRect.bottom = mPaddingEdgeSide ? space : 0;
                outRect.left = mPaddingStart ? space : 0;
            }
        }

    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {

        if (parent.getAdapter() == null) {
            return;
        }

        int orientation = 0;
        int headerCount = 0, footerCount = 0, dataCount;

        if (parent.getAdapter() instanceof BaseQuickAdapter) {
            headerCount = ((BaseQuickAdapter) parent.getAdapter()).getHeaderLayoutCount();
            footerCount = ((BaseQuickAdapter) parent.getAdapter()).getFooterLayoutCount();
            dataCount = parent.getAdapter().getItemCount();
        } else {
            dataCount = parent.getAdapter().getItemCount();
        }
        int dataStartPosition = headerCount;
        int dataEndPosition = headerCount + dataCount;


        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof StaggeredGridLayoutManager) {
            orientation = ((StaggeredGridLayoutManager) layoutManager).getOrientation();
        } else if (layoutManager instanceof GridLayoutManager) {
            orientation = ((GridLayoutManager) layoutManager).getOrientation();
        } else if (layoutManager instanceof LinearLayoutManager) {
            orientation = ((LinearLayoutManager) layoutManager).getOrientation();
        }
        int start, end;
        if (orientation == OrientationHelper.VERTICAL) {
            start = parent.getPaddingLeft();
            end = parent.getWidth() - parent.getPaddingRight();
        } else {
            start = parent.getPaddingTop();
            end = parent.getHeight() - parent.getPaddingBottom();
        }

        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = parent.getChildAt(i);
            int position = parent.getChildAdapterPosition(child);

            if (position >= dataStartPosition && position < dataEndPosition - 1//数据项除了最后一项
                    || (position == dataEndPosition - 1 && mDrawLastItem)//数据项最后一项
                    || (!(position >= dataStartPosition && position < dataEndPosition) && mDrawHeaderFooter)//header&footer且可绘制
                    ) {

                if (orientation == OrientationHelper.VERTICAL) {
                    RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
                    int top = child.getBottom() + params.bottomMargin;
                    int bottom = top;
                    mColorDrawable.setBounds(start, top, end, bottom);
                    mColorDrawable.draw(c);
                } else {
                    RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
                    int left = child.getRight() + params.rightMargin;
                    int right = left;
                    mColorDrawable.setBounds(left, start, right, end);
                    mColorDrawable.draw(c);
                }
            }
        }
    }
}

防止按键速度过快RecyclerView焦点乱飞问题:

FocusLinearLayoutManager.java

package com.example.focusdome.view;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;

public class FocusLinearLayoutManager extends LinearLayoutManager {
    public FocusLinearLayoutManager(Context context) {
        super(context);
    }

    public FocusLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

    public FocusLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }


    @Nullable
    @Override
    public View onInterceptFocusSearch(@NonNull View focused, int direction) {

        Log.v("manger","ssssss");

        int currentPosition = getPosition(getFocusedChild());
        int count = getItemCount();
        int lastVisiblePosition = findLastVisibleItemPosition();

        switch (direction){
            case View.FOCUS_RIGHT:
                currentPosition++;
                break;
            case View.FOCUS_LEFT:
                currentPosition++;
                break;
            case View.FOCUS_DOWN:
                currentPosition++;
                break;
            case View.FOCUS_UP:
                currentPosition++;
                break;

        }

        if (currentPosition<0||currentPosition>count){
            return focused;
        }else {
            if (currentPosition>lastVisiblePosition){
                scrollToPosition(currentPosition);
            }

        }



        return super.onInterceptFocusSearch(focused, direction);
    }
}

 项目地址:https://download.csdn.net/download/qq_41861266/12691503

上一篇:Android 获取 XML 布局位置


下一篇:2.基本控件 带阴影的TextView