前言:
本质和移动端开发没有什么区别,只是要处理遥控器的按键以及焦点。目前我知道有两种技术路径一种使用leanback,一种使用普通移动端的开发方式只不过焦点需要自己处理。
1、页面隐藏状态栏
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
2、组件放大动画
package com.fangtao.widget;
import android.animation.ValueAnimator;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
public class FocusUtil {
private final static int duration = 140;
private final static float startScale = 1.0f;
//private final static float endScale = 1.14f;
public static final float SCALE_RATE = 1.045f;//一
// public static final float SCALE_RATE = 1.0571f;
/**
* 当焦点发生变化
*
* @param view
* @param gainFocus
*/
public static void onFocusChange(View view, boolean gainFocus) {
if (gainFocus) {
onFocusIn(view,SCALE_RATE);
} else {
onFocusOut(view,SCALE_RATE);
}
}
/**
* 当view获得焦点
*
* @param view
*/
public static void onFocusIn(final View view,float endScale) {
ValueAnimator animIn = ValueAnimator.ofFloat(startScale, endScale);
animIn.setDuration(duration);
animIn.setInterpolator(new DecelerateInterpolator());
animIn.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (Float) animation.getAnimatedValue();
view.setScaleX(value);
view.setScaleY(value);
}
});
animIn.start();
}
/**
* 当view失去焦点
*
* @param view
*/
public static void onFocusOut(final View view,float endScale) {
ValueAnimator animOut = ValueAnimator.ofFloat(endScale, startScale);
animOut.setDuration(duration);
animOut.setInterpolator(new DecelerateInterpolator());
animOut.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (Float) animation.getAnimatedValue();
view.setScaleX(value);
view.setScaleY(value);
}
});
animOut.start();
}
}
//使用
FocusUtil.onFocusIn(view, 1.28f);
3、实现基础布局的圆角
在drawable中编写对应的xml来实现
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/greed" /> <!-- 背景颜色,这里设置为透明 -->
<!-- 边框宽度 -->
<!-- 边框颜色 -->
<!-- <stroke-->
<!-- android:width="2dp"-->
<!-- android:color="#9CFFFFFF" />-->
<corners
android:radius="20dp" /> <!-- 圆角半径,根据需要进行调整 -->
</shape>
4、实现轮播功能
开源组件:Android轮播(banner)组件的使用 - 简书
5、实现基础布局的渐变色背景
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:angle="90"
android:endColor="@color/colorPrimary"
android:startColor="@color/colorAccent" />
</shape>
获取焦点和失去焦点改变透明度
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true">
<shape>
<solid android:color="#FF000000" /> <!-- 完全不透明 -->
</shape>
</item>
<item>
<shape>
<solid android:color="#B2000000" /> <!-- 半透明 -->
</shape>
</item>
</selector>
6、实现videoView组件圆角显示
①使用cardView,这种方式四个角都是圆角,无法单独设置每一个圆角
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="5dp"
app:cardElevation="0dp"
app:contentPadding="0dp"
>
<VideoView
android:id="@+id/video"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.cardview.widget.CardView>
②其他方式,暂时没有
7、实现图片圆角
①使用第三方图片组件
参考:Android的ImageView必知必会 - 简书
②使用Glide
RequestOptions options = new RequestOptions()
.placeholder(R.drawable.cache)
.circleCropTransform();
GlideApp.with(FangTaoTvApplication.getInstance())
.load(merchantInfoModel.getPicture())
.placeholder(R.drawable.pic_loading)
.apply(options)
.into(logoImg);
8、布局
参考:约束布局ConstraintLayout看这一篇就够了 - 简书
9、本地缓存工具
参考:Android本地保存实用工具SpUtil 对SharedPreferences的简单封装_android开发中sp工具类
public class SpUtil{
private final SharedPreferences sharedPreferences;
private final SharedPreferences.Editor editor;
private final Gson gson;
private static SpUtil spUtil;
private SpUtil(Context context){
sharedPreferences = context.getSharedPreferences("sp_data",Context.MODE_PRIVATE);
editor = sharedPreferences.edit();
gson = new Gson();
}
public static SpUtilgetInstance (Context context){
if (spUtil == null){
spUtil = new SpUtil(context);
}
return spUtil;
}
}
//保存
public void putInt(String key, int num){
editor.putInt(key, num);
editor.apply();
}
public void putString(String key, String content){
editor.putString(key, content);
editor.apply();
}
public void putBoolean(String key, boolean value){
editor.putBoolean(key, value);
editor.apply();
}
public void putLong(String key, long value){
editor.putLong(key, value);
editor.apply();
}
//读取
public int getInt(String key){
return sharedPreferences.getInt(key, 0);
}
public String getString(String key){
return sharedPreferences.getString(key,"");
}
public boolean getBoolean(String key){
return sharedPreferences.getBoolean(key,false);
}
public long getLong(String key){
return sharedPreferences.getLong(key,0);
}
//保存对象
public void putObject(String key, Object obj){
String json = gson.toJson(obj);
putString(key, json);
}
//获取对象
public <T> T getObject(String key, Class<T> clazz){
String json = getString(key);
if (TextUtils.isEmpty(json)){
return null;
}
return gson.fromJson(json, clazz);
}
//删除对象
public void removeObject(String key){
editor.remove(key);
editor.apply();
}
//保存列表
public void putObjList(String key, List objectList){
String json = gson.toJson(objectList);
putString(key, json);
}
//获取列表
public <T> List<T> getObjList(String key){
List<T> list;
String json = getString(key);
if (TextUtils.isEmpty(json)){
return null;
}
list = gson.fromJson(json, new TypeToken<List<T>>(){}.getType());
return list;
}
/**
* 将map集合转化为json数据保存在sharePreferences中
*
* @param key key sharePreferences数据Key
* @param map map数据
* @return 保存结果
*/
public <K,V> boolean putMap(String key , Map<K,V> map){
boolean result;
try {
String json = gson.toJson(map);
editor.putString(key , json);
editor.apply();
result = true;
}catch (Exception e){
result = false;
e.printStackTrace();
}
return result;
}
/**
* 从本地获取保存的map数据
* 缺点:将Integer默认转化为Double
* */
public <K,V> HashMap<K,V> getMap(String key){
String mapJson = getString(key);
if (TextUtils.isEmpty(mapJson)){
return null;
}
return gson.fromJson(mapJson, new TypeToken<HashMap<K,V>>(){}.getType());
}
基本使用
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SpUtil spUtil= SpUtil.getInstance(this);
//简单类型存取
spUtil.putInt("first", 1);
spUtil.getInt("first");
//hashMap存取
HashMap<String , Integer> map = new HashMap<>();
map.put("铁柱",30);
map.put("小凤",28);
map.put("阿珍",26);
map.put("阿强",27);
if (spUtil.putMap("MAP_DATA",map)){
HashMap<String , Integer> hashMap = spUtil.getMap("MAP_DATA");
if (hashMap != null ){
spUtil.showMapData(hashMap);
}
}else {
Log.e(TAG, "1111" );
}
}
10、常用监听
①焦点监听
//搜索
flTopSearch.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View view, boolean b) {
if(b) { //选中图片
GlideApp.with(MainActivity.getActivity()).asDrawable()
.load(R.drawable.home_search_icon_select_v6)
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.into(new ImageViewTarget<Drawable>(ivSearchIcon) {
@Override
protected void setResource(@Nullable Drawable resource) { if(resource!=null) {
ivSearchIcon.setImageDrawable(resource);
}
}
});
}else{ //未选中图片
GlideApp.with(MainActivity.getActivity()).asDrawable()
.load(R.drawable.home_search_icon_unselect_v6)
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.into(new ImageViewTarget<Drawable>(ivSearchIcon) {
@Override
protected void setResource(@Nullable Drawable resource) { if(resource!=null) {
ivSearchIcon.setImageDrawable(resource);
}
}
});
}
}
});
②遥控器按键监听
cflVideo.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View view, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_LEFT:
View view1 = findViewById(R.id.cfl_banner);
view1.requestFocus();
view1.setFocusable(true);
view1.setFocusableInTouchMode(true);
return true;
case KeyEvent.KEYCODE_DPAD_RIGHT:
CustomFrameLayout layout = findViewById(R.id.qcode);
layout.requestFocus();
layout.setFocusable(true);
layout.setFocusableInTouchMode(true);
return true;
case KeyEvent.KEYCODE_DPAD_UP:
TextView button = findViewById(R.id.cfl_my_top_day_update);
button.requestFocus();
button.setFocusable(true);
button.setFocusableInTouchMode(true);
return true;
case KeyEvent.KEYCODE_DPAD_DOWN:
CustomFrameLayout layout1 = findViewById(R.id.cfl_all_commodity);
layout1.requestFocus();
layout1.setFocusable(true);
layout1.setFocusableInTouchMode(true);
return true;
case KeyEvent.KEYCODE_DPAD_CENTER:
if(MusicService.getService().getStatus()){
MusicService.getService().pause();
}
Intent intent = new Intent(MainActivity.this, FullVideoActivity.class);
intent.putExtra("videoUrl",re);
intent.putExtra("type", type);
startActivity(intent);
return true;
case KeyEvent.KEYCODE_BACK:
break;
}
}
return false;
}
});
11、代码中选中组件
cflAllGoods.requestFocus();
cflAllGoods.setFocusable(true); //可以被按键选中
cflAllGoods.setFocusableInTouchMode(true); //可以被触摸选中