Android TV item 选中 放大,加边框

来源:互联网 发布:json date 格式化注解 编辑:程序博客网 时间:2024/06/07 06:13

Android Tv 的交互是通过遥控器来进行的,焦点移动是用户操作后的直观感受。如何让用户直观的操控Tv是本文的重点介绍内容。
1. Tv开发一般都会有自定义的Launcher,launcher中显示媒体资源数据。类似下图
这里写图片描述
每个item选中后会有个边框,并且会变大。下面将如何实现这个效果。

放大效果

首先item的布局,以LinearLayout为例,我们要的效果是当LinearLayout或者Imageview 获取焦点时, LinearLayout放大,当焦点移动到其他item时,此item丢失焦点,恢复原来的大小。所以我们自定义LinearLayout。

public class CustomLinearLayout extends LinearLayout implements View.OnFocusChangeListener, CustomViewInterface, View.OnHoverListener {    CustomFocusedChanged focusedChanged;    public CustomLinearLayout(Context context) {        super(context);        init();    }    public CustomLinearLayout(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);        init();    }    public CustomLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {        super(context, attrs, defStyleAttr, defStyleRes);        init();    }    public void setFocusedChanged(CustomFocusedChanged focusedChanged) {        this.focusedChanged = focusedChanged;    }    @Override    public void init() {        this.setOnFocusChangeListener(this);        this.setOnHoverListener(this);    }    @Override    public void onFocusChange(View v, boolean hasFocus) {        if (focusedChanged != null) {            focusedChanged.onFocusChange(v, hasFocus);//外部接口调用        }        DisplayUtil.setViewAnimator(v, hasFocus);//放大缩小动画    }    //鼠标事件处理    @Override    public boolean onHover(View v, MotionEvent event) {        if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER) {            DisplayUtil.setViewAnimator(v, true);        } else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) {            DisplayUtil.setViewAnimator(v, false);        }        return false;    }}

接口介绍

public interface CustomViewInterface {    void init();    void setFocusedChanged(CustomFocusedChanged focusedChanged);}

此接口作用是因为我们的自定义控件获取焦点,当外部需要监听其焦点变化时,若再调用View.OnFocusChangeListener接口会覆盖掉我们自定义控件中的监听,所有再写一个接口供外部调用。

View.OnHoverListener接口未监听鼠标接口
实现onHover 方法

@Override    public boolean onHover(View v, MotionEvent event) {        if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER) {            //鼠标进入控件范围        } else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) {            //鼠标离开控件        }        return false;    }

放大动画

public static final float ZOOM_SCALE = 1.05f;private static final float ORIGIN_SCALE = 1.0f; public static void setViewAnimator(View v, boolean focus) {        setViewAnimator(v, focus, ZOOM_SCALE);    }    public static void setViewAnimator(View v, boolean focus, float... params) {        AnimatorSet animatorSet = new AnimatorSet();//组合动画        ObjectAnimator scaleX;        ObjectAnimator scaleY;        if (focus) {            scaleX = ObjectAnimator.ofFloat(v, "scaleX", ORIGIN_SCALE, params[0]);            scaleY = ObjectAnimator.ofFloat(v, "scaleY", ORIGIN_SCALE, params[0]);        } else {            scaleX = ObjectAnimator.ofFloat(v, "scaleX", params[0], ORIGIN_SCALE);            scaleY = ObjectAnimator.ofFloat(v, "scaleY", params[0], ORIGIN_SCALE);        }        if (params.length > 1) {            v.setPivotX(params[1]);            v.setPivotY(params[2]);        }        animatorSet.setDuration(300);        animatorSet.setInterpolator(new DecelerateInterpolator());        animatorSet.play(scaleX).with(scaleY);//两个动画同时开始        animatorSet.start();    }

圆角效果

重点类:RoundDrawable,ColorStateList

public class RoundLinearLayout extends LinearLayout{    private ColorStateList mSolidColor;    private int mCornerRadius;    public RoundLinearLayout(Context context) {        super(context);    }    public RoundLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    public RoundLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {        super(context, attrs, defStyleAttr, defStyleRes);    }    public RoundLinearLayout(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RoundButton);        float pressedRatio = a.getFloat(R.styleable.RoundButton_btnPressedRatio, 0.80f);        mCornerRadius = a.getLayoutDimension(R.styleable.RoundButton_btnCornerRadius, getResources().getDimensionPixelSize(R.dimen.radius_default));        mSolidColor = a.getColorStateList(R.styleable.RoundButton_btnSolidColor);        int strokeColor = a.getColor(R.styleable.RoundButton_btnStrokeColor, Color.GRAY);        int strokeWidth = a.getDimensionPixelSize(R.styleable.RoundButton_btnStrokeWidth, 0);        int strokeDashWidth = a.getDimensionPixelSize(R.styleable.RoundButton_btnStrokeDashWidth, 0);        int strokeDashGap = a.getDimensionPixelSize(R.styleable.RoundButton_btnStrokeDashGap, 0);        a.recycle();        RoundDrawable rd = new RoundDrawable(mCornerRadius == -1);        rd.setCornerRadius(mCornerRadius == -1 ? 0 : mCornerRadius);        rd.setStroke(strokeWidth, strokeColor, strokeDashWidth, strokeDashGap);        if (mSolidColor == null) {            mSolidColor = ColorStateList.valueOf(Color.GRAY);        }        if (mSolidColor.isStateful()) {            rd.setSolidColors(mSolidColor);        } else if (pressedRatio > 0.0001f) {            rd.setSolidColors(csl(mSolidColor.getDefaultColor(), pressedRatio));        } else {            rd.setColor(mSolidColor.getDefaultColor());        }        setBackground(rd);    }    public void setSolidColor(int color) {        RoundDrawable rd = new RoundDrawable(mCornerRadius == -1);        rd.setCornerRadius(mCornerRadius == -1 ? 0 : mCornerRadius);        mSolidColor = ColorStateList.valueOf(color);        if (mSolidColor.isStateful()) {            rd.setSolidColors(mSolidColor);        }  else {            rd.setColor(mSolidColor.getDefaultColor());        }        setBackground(rd);    }    int darker(int color, float ratio) {        color = (color >> 24) == 0 ? 0x22808080 : color;        float[] hsv = new float[3];        Color.colorToHSV(color, hsv);        hsv[2] *= ratio;        return Color.HSVToColor(color >> 24, hsv);    }    ColorStateList csl(int normal, float ratio) {        //        int disabled = greyer(normal);        int pressed = darker(normal, ratio);        int[][] states = new int[][]{{android.R.attr.state_pressed}, {}};        int[] colors = new int[]{pressed, normal};        return new ColorStateList(states, colors);    }

attrs文件

<declare-styleable name="RoundButton">        <!-- 背景色 -->        <attr name="btnSolidColor" format="color"/>        <!-- 边框色 -->        <attr name="btnStrokeColor" format="color"/>        <!-- 边框厚度 -->        <attr name="btnStrokeWidth" format="dimension"/>        <!-- 边框虚线长度 -->        <attr name="btnStrokeDashWidth" format="dimension"/>        <!-- 边框虚线间隙 -->        <attr name="btnStrokeDashGap" format="dimension"/>        <!-- 圆角半径,stadium 表示半径为 min(height,width) / 2-->        <attr name="btnCornerRadius" format="dimension">            <enum name="stadium" value="-1"/>        </attr>        <attr name="btnPressedRatio" format="float"/>    </declare-styleable>

设置边框

创建item_background_focus.xml

<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android">    <stroke        android:width="@dimen/cibn_view_stroke_width"        android:color="@color/item_stroke_focus_color"/>    <corners        android:bottomLeftRadius="@dimen/radius_default"//圆角与RoundLinearLayout中的角度一致        android:bottomRightRadius="@dimen/radius_default"        android:topLeftRadius="@dimen/radius_default"        android:topRightRadius="@dimen/radius_default"/>    <padding        android:bottom="@dimen/cibn_view_stroke_padding"        android:left="@dimen/cibn_view_stroke_padding"        android:right="@dimen/cibn_view_stroke_padding"        android:top="@dimen/cibn_view_stroke_padding"/></shape>

创建item_background_normal.xml

<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android">    <stroke        android:width="@dimen/cibn_view_stroke_width"        android:color="@color/item_stroke_color"/>    <corners        android:bottomLeftRadius="@dimen/radius_default"        android:bottomRightRadius="@dimen/radius_default"        android:topLeftRadius="@dimen/radius_default"        android:topRightRadius="@dimen/radius_default"/>    <padding        android:bottom="@dimen/cibn_view_stroke_padding"        android:left="@dimen/cibn_view_stroke_padding"        android:right="@dimen/cibn_view_stroke_padding"        android:top="@dimen/cibn_view_stroke_padding"/></shape>

创建item_sleclected.xml

<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android">    <item android:drawable="@drawable/item_background_focus" android:state_focused="true"/>    <item android:drawable="@drawable/item_background_normal" android:state_focused="false"/></selector>

设置item的背景为item_sleclected.xml即可
将自定义的LinearLayout 继承RoundLinearLayout ,即为最终控件。将布局文件中的LinearLayout替换成我们的自定义LinearLayout即为最终效果。(前提是焦点问题要处理好,焦点处理需要注意的点后面会单独介绍)

原创粉丝点击