AndroidTV开发7————实现仿小米电视和各种盒子TV焦点放大缩小效果
AndroidTV中最常见的就是焦点效果,这个实现很简单,在代码设置view.setFocusable(true),布局中focusable="true",然后添加view获得焦点时的效果即可。但是本文不讲这些,而是讲解View获取焦点有一个放大缩小效果,当view放大时处于临近的View之上并且不能被遮挡,当view缩小时要恢复原来的大小。
1.先放几张效果图:
2.方案分析,网上的方案和资料如下:
(1):MainUpView:
(2):MetroItemFrameLayout(何俊林大神的方案):博客地址:https://blog.csdn.net/hejjunlin/article/details/52792562
(3)Android tv metro,项目地址:https://gitee.com/kumei/android_tv_metro
(4)Android tv frame,项目地址:https://gitee.com/kumei/android-tv-frame-new
(5)TvRecyclerView,项目地址:https://github.com/henryblue/TvRecyclerView
(6)TvRecyclerView,项目地址:https://github.com/zhousuqiang/TvRecyclerView
(7)TVRecyclerView,项目地址:https://github.com/Fredlxy/TVRecyclerView
(8)Android tv frame,项目地址:https://github.com/songwenju/CustomTvRecyclerView
(9)TvWidget,项目地址:https://github.com/evilbinary/TvWidget
(10)BorderViewDemo,项目地址:https://github.com/lf8289/BorderViewDemo
(11)TvFocusBorder,项目地址:https://github.com/zhousuqiang/TvFocusBorder
(12) Android Tv Widget Demo,项目地址: https://github.com/zhousuqiang/TvWidget
(13)SMTVLauncher,项目地址:https://github.com/FrozenFreeFall/SMTVLauncher
(14)AndroidTVLauncher,项目地址:https://github.com/JackyAndroid/AndroidTVLauncher
(15)TVSample,项目地址:https://github.com/hejunlin2013/TVSample
(16)Leanback桌面demo,项目地址:https://gitee.com/chuangshiji/Launcher
3.以上方案尝试了一半,由于时间和项目周期问题,所以有些没来得及尝试,而且接入成本有点大,每个项目都不一样,选择适合自己的才最重要,我们不要拘泥于某一种方案或者第三方库.这里我根据以上方案总结出一份属于我自己的方案,主要代码如下:
4.Fragment完整代码如下:
package com.example.tvrecyclerview.fragment;
import android.os.Build;
import android.view.View;
import android.widget.ImageView;
import com.example.tvrecyclerview.R;
/**
* @author: njb
* @date: 2020/9/24 0024 0:47
* @desc:
*/
public class OtherFragment extends FragmentBase implements View.OnFocusChangeListener {
private static final float scale0 = 1f;
private static final float scaleL = 1.12f;
private static final float scaleS = 1.22f;
private static final float z1 = 0f;
private static final float z2 = 10f;
private static final long animateDuration = 20;
private static View lastFocus;
private ImageView ivRecentControl, ivCollectControl, ivRemoteControl, ivHandleControl, ivQrCodeControl;
@Override
protected int getLayoutRes() {
return R.layout.fragment_other;
}
@Override
protected void rootViewNoNull() {
super.rootViewNoNull();
if(null != lastFocus){
lastFocus.requestFocus();
}
}
@Override
protected void initView(View rootView) {
super.initView(rootView);
//初始化控件
ivRecentControl = rootView.findViewById(R.id.iv_my_recent);
ivCollectControl = rootView.findViewById(R.id.iv_my_collect);
ivHandleControl = rootView.findViewById(R.id.iv_my_handle);
ivRemoteControl = rootView.findViewById(R.id.iv_my_remote);
ivQrCodeControl = rootView.findViewById(R.id.iv_my_qrcode);
//初始化焦点事件
ivRecentControl.setOnFocusChangeListener(this);
ivCollectControl.setOnFocusChangeListener(this);
ivRemoteControl.setOnFocusChangeListener(this);
ivHandleControl.setOnFocusChangeListener(this);
ivQrCodeControl.setOnFocusChangeListener(this);
}
@Override
public void onFocusChange(final View v, boolean hasFocus) {
//初始化放大倍数
float scaleX1, scaleX2, scaleY1, scaleY2;
scaleX1 = scaleY1 = scaleX2 = scaleY2 = scale0;
//给获取焦点的view设置倍数
if (v.getId() == R.id.iv_my_recent || v.getId() == R.id.iv_my_collect) {
scaleX2 = scaleY2 = scaleL;
}
if (v.getId() == R.id.iv_my_handle || v.getId() == R.id.iv_my_remote || v.getId() == R.id.iv_my_qrcode) {
scaleX2 = scaleY2 = scaleS;
}
//获得焦点时的状态和倍数
if (hasFocus) {
lastFocus = v;
//在android5.0以上的效果
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
v.animate()
.scaleXBy(scaleX1)
.scaleX(scaleX2)
.scaleYBy(scaleY1)
.scaleY(scaleY2)
.zBy(z1)
.z(z2)
.setDuration(animateDuration);
} else {
//在android5.0以下的效果
v.animate()
.scaleXBy(scaleX1)
.scaleX(scaleX2)
.scaleYBy(scaleY1)
.scaleY(scaleY2)
.withEndAction(new Runnable() {
@Override
public void run() {
//防止view被遮挡
v.bringToFront();
}
})
.setDuration(animateDuration);
}
} else {
//view在焦点消失后的倍数和状态
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
v.animate()
.scaleXBy(scaleX2)
.scaleX(scaleX1)
.scaleYBy(scaleY2)
.scaleY(scaleY1)
.zBy(z2)
.z(z1)
.setDuration(animateDuration);
} else {
v.animate()
.scaleXBy(scaleX2)
.scaleX(scaleX1)
.scaleYBy(scaleY2)
.scaleY(scaleY1)
.setDuration(animateDuration);
}
}
}
}
5.布局文件fragment_other.xml代码:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_blue">
<include
layout="@layout/layout_more_game"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
6.layout_more_game.xml代码:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/cl_more"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp"
android:layout_marginTop="16dp"
android:clipChildren="false"
android:clipToPadding="false"
tools:ignore="MissingConstraints">
<ImageView
android:id="@+id/iv_my_recent"
android:layout_width="91dp"
android:layout_height="111dp"
android:background="@drawable/bg_more_focus"
android:focusable="true"
android:padding="2dp"
android:scaleType="fitXY"
android:clipChildren="false"
android:clipToPadding="false"
android:src="@mipmap/ic_recent"
android:layout_marginEnd="16dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/iv_my_collect"/>
<ImageView
android:id="@+id/iv_my_collect"
android:layout_width="91dp"
android:layout_height="111dp"
android:layout_marginEnd="16dp"
android:background="@drawable/bg_more_focus"
android:focusable="true"
android:padding="2dp"
android:scaleType="fitXY"
android:clipChildren="false"
android:clipToPadding="false"
android:src="@mipmap/ic_collect"
app:layout_constraintLeft_toRightOf="@+id/iv_my_recent"
app:layout_constraintRight_toLeftOf="@id/iv_my_handle"/>
<ImageView
android:id="@+id/iv_my_handle"
android:layout_width="0dp"
android:layout_height="111dp"
android:layout_marginEnd="16dp"
android:background="@drawable/bg_more_focus"
android:focusable="true"
android:padding="2dp"
android:scaleType="fitXY"
android:clipChildren="false"
android:clipToPadding="false"
android:src="@mipmap/ic_handle_game"
app:layout_constraintLeft_toRightOf="@+id/iv_my_collect"
app:layout_constraintRight_toLeftOf="@+id/iv_my_remote"/>
<ImageView
android:id="@+id/iv_my_remote"
android:layout_width="0dp"
android:layout_height="111dp"
android:background="@drawable/bg_more_focus"
android:focusable="true"
android:padding="2dp"
android:scaleType="fitXY"
android:layout_marginEnd="16dp"
android:clipChildren="false"
android:clipToPadding="false"
android:src="@mipmap/ic_remote_control"
app:layout_constraintLeft_toRightOf="@+id/iv_my_handle"
app:layout_constraintRight_toLeftOf="@+id/iv_my_qrcode"/>
<ImageView
android:id="@+id/iv_my_qrcode"
android:layout_width="0dp"
android:layout_height="111dp"
android:background="@drawable/bg_more_focus"
android:focusable="true"
android:padding="2dp"
android:scaleType="fitXY"
android:clipChildren="false"
android:clipToPadding="false"
android:src="@mipmap/ic_qrcode_control"
app:layout_constraintLeft_toRightOf="@+id/iv_my_remote"
app:layout_constraintRight_toRightOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
7.实现的效果图如下:
8.简单分析:要想 实现以上效果需要考虑几个问题:
a.放大后的倍数
b.缩小后的倍数
c.view处于临界view之上的边界被切问题
d.在android5.0以上和5.0以下的适配问题
e.封装成一个工具类,因为现在的界面基本上都很复杂,如果是多个布局需要写多个view和倍数等,这样的话每个界面都要写很多次,感觉都是做重复的工作。
9.以上就是TV盒子上的焦点效果实现过程和原理简单分析,后面会出一篇博客详细分析怎么计算倍数,下一篇将会分析怎么封装成一个工具类和封装遇到的问题,欢迎小伙伴们前来讨论留言,有错误指正出来,我会及时更改。
项目的源码地址如下:https://gitee.com/jackning_admin/TvRecyclerView
本文地址:https://blog.csdn.net/u012556114/article/details/108860875
上一篇: 百度音乐好听的歌曲怎么分享到空间?