从初TV开发到现在,在移动边框上用过很多方法。 下面我来简单的列出来使用过那些解决方法和思路:
下面让我们来进入我的框架的主题来看一下:
红圈所标出来的是几个主要的类与自定义View,下面我们来深入(我在设计的时候,焦点处理是各自处理各自的,解耦)。 先上两幅比较难的界面(重点在于焦点的处理与动画的处理,图一有动态的添加和删除)。
最主要的接口MoveAnimationHelper(做动画效果的)如下:/**
* Created by ShanCanCan on 2016/4/3 0003.
*/
public interface MoveAnimationHelper {
voiddrawMoveView(Canvas canvas);//绘制MoveView
voidsetFocusView(View currentView, View oldView,floatscale); //放大缩小函数
voidrectMoveAnimation(View currentView,floatscaleX,floatscaleY);// 边框移动函数
MoveFrameLayoutgetMoveView(); //边框view
voidsetMoveView(MoveFrameLayout moveView);//setMoveView
voidsetTranDurAnimTime(inttime);//设置移动时间
voidsetDrawUpRectEnabled(boolean isDrawUpRect);//是否凸出显示
}
MoveFrameLayout是全局的移动飞框,就像文章开头的1的实现类似,但是全局只有一个。最主要的绘制函数就是 MoveFrameLayout 这个类了,这个类就是我们的边框移动 View,这个 View 主要实现边框的生成与移动,还有阴影的添加 /**
*
* Created by ShanCanCan on 2016/4/3 0003.
*/
public classMoveFrameLayoutextendsFrameLayout{
private static final String TAG = "MoveFramLayout";
private Context mContext;
private Drawable mRectUpDrawable;
private Drawable mRectUpShade;
private MoveAnimationHelper mMoveAnimationHelper;
private RectF mShadowPaddingRect = new RectF();
private RectF mUpPaddingRect = new RectF();
publicMoveFrameLayout(Context context){
super(context);
init(context);
}
publicMoveFrameLayout(Context context, AttributeSet attrs){
super(context, attrs);
init(context);
}
publicMoveFrameLayout(Context context, AttributeSet attrs,intdefStyleAttr){
super(context, attrs, defStyleAttr);
init(context);
}
privatevoidinit(Context context){
mContext = context;
setWillNotDraw(false);//必须要设置,如果我们想要重写onDraw,就要调用setWillNotDraw(false)
mMoveAnimationHelper = new MoveAnimationHelperImplement();//动画的实现类,接下来就要讲解
mMoveAnimationHelper.setMoveView(this);
}
/*下面的方法基本是调用MoveAnimationHelperImplement的实现方法,来进行我们的放大缩小以及其他展示*/
publicvoidsetFocusView(View currentView, View oldView,floatscale){
mMoveAnimationHelper.setFocusView(currentView, oldView, scale);
}
publicViewgetUpView(){
return this;
}
@Override
protectedvoidonDraw(Canvas canvas){
if (mMoveAnimationHelper != null) {
mMoveAnimationHelper.drawMoveView(canvas);
return;
}
super.onDraw(canvas);
}
publicvoidsetUpRectResource(intid){
try {
this.mRectUpDrawable = mContext.getResources().getDrawable(id); // 移动的边框.
invalidate();
} catch (Exception e) {
e.printStackTrace();
}
}
publicvoidsetUpRectShadeResource(intid){
this.mRectUpShade = mContext.getResources().getDrawable(id); // 移动的边框.
invalidate();
}
publicDrawablegetShadowDrawable(){
return this.mRectUpShade;
}
publicDrawablegetUpRectDrawable(){
return this.mRectUpDrawable;
}
publicRectFgetDrawShadowRect(){
return this.mShadowPaddingRect;
}
publicRectFgetDrawUpRect(){
return this.mUpPaddingRect;
}
publicvoidsetUpPaddingRect(RectF upPaddingRect){
mUpPaddingRect = upPaddingRect;
}
publicvoidsetShadowPaddingRect(RectF shadowPaddingRect){
mShadowPaddingRect = shadowPaddingRect;
}
publicvoidsetTranDurAnimTime(intdefaultTranDurAnim){
mMoveAnimationHelper.setTranDurAnimTime(defaultTranDurAnim);
}
publicvoidsetDrawUpRectEnabled(booleanisDrawUpRect){
mMoveAnimationHelper.setDrawUpRectEnabled(isDrawUpRect);
}
}
MoveAnimationHelperImplement,MoveAnimationHelper的实现者。这是这个类里面最主要的方法setFocusView。@Override
public voiddrawMoveView(Canvas canvas){
canvas.save();
if (!isDrawUpRect) {//飞框的绘制顺序,
onDrawShadow(canvas);
onDrawUpRect(canvas);
}
// 绘制焦点子控件.
if (mFocusView != null && (!isDrawUpRect && isDrawing)) {
onDrawFocusView(canvas);
}
//
if (isDrawUpRect) {//飞框的绘制顺序
onDrawShadow(canvas);
onDrawUpRect(canvas);
}
canvas.restore();
}
@Override
public voidsetFocusView(View currentView, View oldView,floatscale){
mFocusView = currentView;
int getScale = (int) (scale * 10);
if (getScale > 10) {
if (currentView != null) {
currentView.animate().scaleX(scale).scaleY(scale).setDuration(DEFAULT_TRAN_DUR_ANIM).start();
if (oldView != null) {
oldView.animate().scaleX(DEFUALT_SCALE).scaleY(DEFUALT_SCALE).setDuration(DEFAULT_TRAN_DUR_ANIM).start();
}
}
}
rectMoveAnimation(currentView, scale, scale);
}
@Override
public voidrectMoveAnimation(View currentView,floatscaleX,floatscaleY){
Rect fromRect = findLocationWithView(getMoveView());
Rect toRect = findLocationWithView(currentView);
int disX = toRect.left - fromRect.left;
int disY = toRect.top - fromRect.top;
rectMoveMainLogic(currentView, disX, disY, scaleX, scaleY);
}
private RectfindLocationWithView(View view){
ViewGroup root = (ViewGroup) getMoveView().getParent();
Rect rect = new Rect();
root.offsetDescendantRectToMyCoords(view, rect);
return rect;
}
private voidrectMoveMainLogic(finalView focusView,floatx,floaty,floatscaleX,floatscaleY){
int newWidth = 0;
int newHeight = 0;
int oldWidth = 0;
int oldHeight = 0;
if (focusView != null) {
newWidth = (int) (focusView.getMeasuredWidth() * scaleX);
newHeight = (int) (focusView.getMeasuredHeight() * scaleY);
x = x + (focusView.getMeasuredWidth() - newWidth) / 2;
y = y + (focusView.getMeasuredHeight() - newHeight) / 2;
}
// 取消之前的动画.
if (mCombineAnimatorSet != null)
mCombineAnimatorSet.cancel();
oldWidth = getMoveView().getMeasuredWidth();
oldHeight = getMoveView().getMeasuredHeight();
ObjectAnimator transAnimatorX = ObjectAnimator.ofFloat(getMoveView(), "translationX", x);
ObjectAnimator transAnimatorY = ObjectAnimator.ofFloat(getMoveView(), "translationY", y);
ObjectAnimator scaleXAnimator = ObjectAnimator.ofInt(new ScaleTool(getMoveView()), "width", oldWidth,
(int) newWidth);
ObjectAnimator scaleYAnimator = ObjectAnimator.ofInt(new ScaleTool(getMoveView()), "height", oldHeight,
(int) newHeight);
//
AnimatorSet mAnimatorSet = new AnimatorSet();
mAnimatorSet.playTogether(transAnimatorX, transAnimatorY, scaleXAnimator, scaleYAnimator);
mAnimatorSet.setInterpolator(new DecelerateInterpolator(1));
mAnimatorSet.setDuration(DEFAULT_TRAN_DUR_ANIM);
getMoveView().setVisibility(View.VISIBLE);
mAnimatorSet.addListener(new Animator.AnimatorListener() {//动画的监听,主要用来设置移动飞框的绘制顺序
@Override
public voidonAnimationStart(Animator animation){
if (!isDrawUpRect)
isDrawing = false;
}
@Override
public voidonAnimationRepeat(Animator animation){
if (!isDrawUpRect)
isDrawing = false;
}
@Override
public voidonAnimationEnd(Animator animation){
if (!isDrawUpRect)
isDrawing = true;
}
@Override
public voidonAnimationCancel(Animator animation){
if (!isDrawUpRect)
isDrawing = false;}
});
mAnimatorSet.start();
mCombineAnimatorSet = mAnimatorSet;
}
下面是绘制边框和绘制阴影的方法,这次方法中可以动态的调节移动边框的大小,实现全包裹或者是类似于padding的效果。 /**
* 绘制最上层的移动边框.
*/
public void onDrawUpRect(Canvas canvas) {
Drawable drawableUp = getMoveView().getUpRectDrawable();
if (drawableUp != null) {
RectF paddingRect = getMoveView().getDrawUpRect();//从MoveView()中获取的,你可以自己在activity调节。
int width = getMoveView().getWidth();
int height = getMoveView().getHeight();
Rect padding = newRect();
// 边框的绘制.
drawableUp.getPadding(padding);
drawableUp.setBounds((int) (-padding.left + (paddingRect.left)), (int) (-padding.top + (paddingRect.top)),
(int) (width + padding.right - (paddingRect.right)), (int) (height + padding.bottom - (paddingRect.bottom)));
drawableUp.draw(canvas);
}
}
/**
* 绘制外部阴影.
*/
public void onDrawShadow(Canvas canvas) {
Drawable drawableShadow = getMoveView().getShadowDrawable();
if (drawableShadow != null) {
RectF shadowPaddingRect = getMoveView().getDrawShadowRect();//从MoveView()中获取的,你可以自己在activity调节。
int width = getMoveView().getWidth();
int height = getMoveView().getHeight();
Rect padding = new Rect();
drawableShadow.getPadding(padding);
drawableShadow.setBounds((int) (-padding.left + (shadowPaddingRect.left)), (int) (-padding.top + (shadowPaddingRect.top)),
(int) (width + padding.right - (shadowPaddingRect.right)),
(int) (height + padding.bottom - (shadowPaddingRect.bottom)));
drawableShadow.draw(canvas);
}
}
根部局所采用的方法是继承RelativeLayout最上层的layout ,用来包裹我们所有的控件,这样,主要是为了放大的时候,控件不会被挡住package com.shancancan.tvdemos.views;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.RelativeLayout;
/**
*
* Created by ShanCanCan on 2016/4/3 0003.
*/
public classMainRelativeLayoutextendsRelativeLayout{
private int position;
publicMainRelativeLayout(Context context){
super(context);
init(context);
}
publicMainRelativeLayout(Context context, AttributeSet attrs){
super(context, attrs);
init(context);
}
publicMainRelativeLayout(Context context, AttributeSet attrs,intdefStyleAttr){
super(context, attrs, defStyleAttr);
init(context);
}
privatevoidinit(Context context){
setClipChildren(false); //是否现限制其他控件在它周围绘制选择false
setClipToPadding(false); //是否限制控件区域在padding里面
setChildrenDrawingOrderEnabled(true);//用于改变控件的绘制顺序
getViewTreeObserver()
.addOnGlobalFocusChangeListener(new ViewTreeObserver.OnGlobalFocusChangeListener() {
@Override
publicvoidonGlobalFocusChanged(View oldFocus, View newFocus){
position = indexOfChild(newFocus);
if (position != -1) {
bringChildToFront(newFocus);
newFocus.postInvalidate();// 然后让控件重画,这样会好点。
}
}
});
}
/**
* 此函数 dispatchDraw 中调用.
* 原理就是和最后一个要绘制的view,交换了位置.
* 因为dispatchDraw最后一个绘制的view是在最上层的.
* 这样就避免了使用 bringToFront 导致焦点错乱问题.
*/
@Override
protectedintgetChildDrawingOrder(intchildCount,inti){
if (position != -1) {
if (i == childCount - 1){
return position;
}
if (i == position)
return childCount - 1;
}
return i;
}
}
使用方法两步走:一,布局文件<?xml version="1.0"encoding="utf-8"?>
<com.shancancan.tvdemos.views.MainRelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_entry"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:background="@drawable/default_wallpaper"
tools:context="com.shancancan.tvdemos.activities.EntryActivity">
<!-布局文件最下方介绍-->
<com.shancancan.tvdemos.views.RoundImageView
android:layout_width="200dp"
android:layout_height="200dp"
android:scaleType="fitXY"
android:focusable="true"
app:borderRadius="5dp"
app:type="round"
android:src="@drawable/beijing7"
android:layout_alignParentBottom="true"
android:layout_alignParentStart="true"
android:layout_marginBottom="28dp"
android:id="@+id/roundImageView3"/>
<com.shancancan.tvdemos.views.RoundImageView
android:layout_width="200dp"
android:layout_height="200dp"
app:borderRadius="5dp"
app:type="round"
android:focusable="true"
android:scaleType="fitXY"
android:src="@drawable/beijing5"
android:layout_alignTop="@+id/roundImageView3"
android:layout_alignStart="@+id/roundImageView4"
android:id="@+id/roundImageView5"/>
<com.shancancan.tvdemos.views.RoundImageView
android:layout_width="400dp"
android:layout_height="200dp"
app:borderRadius="5dp"
app:type="round"
android:focusable="true"
android:scaleType="fitXY"
android:src="@drawable/beijing3"
android:id="@+id/roundImageView7"
android:layout_alignTop="@+id/roundImageView5"
android:layout_toEndOf="@+id/roundImageView5"
android:layout_marginStart="117dp"/>
<com.shancancan.tvdemos.views.RoundImageView
android:layout_width="200dp"
android:layout_height="200dp"
app:borderRadius="15dp"
app:type="round"
android:focusable="true"
android:scaleType="fitXY"
android:src="@drawable/beijing1"
android:id="@+id/roundImageView2"/>
<com.shancancan.tvdemos.views.RoundImageView
android:layout_width="200dp"
android:layout_height="200dp"
app:borderRadius="5dp"
app:type="round"
android:focusable="true"
android:scaleType="fitXY"
android:src="@drawable/beijing4"
android:id="@+id/roundImageView6"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_marginEnd="128dp"/>
<com.shancancan.tvdemos.views.RoundImageView
android:layout_width="300dp"
android:layout_height="100dp"
android:scaleType="fitXY"
android:src="@drawable/beijing6"
app:borderRadius="5dp"
app:type="round"
android:focusable="true"
android:id="@+id/roundImageView4"
android:layout_alignBottom="@+id/roundImageView2"
android:layout_toStartOf="@+id/roundImageView6"
android:layout_marginEnd="34dp"/>
<!--MoveFrameLayout必须在根布局之上,而且不能被其他的控件位置上有引用-->
<com.shancancan.tvdemos.views.MoveFrameLayout
android:id="@+id/entrymove"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</com.shancancan.tvdemos.views.MoveFrameLayout>
<!--根布局用MainRelativeLayout-->
</com.shancancan.tvdemos.views.MainRelativeLayout>
二,activity处理public classEntryActivityextendsAppCompatActivity{
private static final String TAG = "MainActivity";
MainRelativeLayout mRelativeLayout;
MoveFrameLayout mMoveView;
View mOldFocus;
@Override
protectedvoidonCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_entry);
mRelativeLayout = (MainRelativeLayout) findViewById(R.id.activity_entry);
mMoveView = (MoveFrameLayout) findViewById(R.id.entrymove);
mMoveViewsetDetail();
initRelativeLayout();
}
privatevoidmMoveViewsetDetail(){
mMoveView.setUpRectResource(R.drawable.conner);//这里也可以设置shape或者是.9图片
float density = getResources().getDisplayMetrics().density;//调整大小,如果你的边框大了就修改w_或者h_这两个参数
RectF receF = new RectF(-getDimension(R.dimen.w_5) * density, -getDimension(R.dimen.h_5) * density,
-getDimension(R.dimen.w_5) * density, -getDimension(R.dimen.h_5) * density);
mMoveView.setUpPaddingRect(receF);//重新为mMoveView设置大小
mMoveView.setTranDurAnimTime(400);
}
publicfloatgetDimension(intid){
return getResources().getDimension(id);
}
privatevoidinitRelativeLayout(){//这是焦点的全局监听方法,与OnFocusChangeListener不同,这个方法长安不执行。
mRelativeLayout.getViewTreeObserver().addOnGlobalFocusChangeListener(new ViewTreeObserver.OnGlobalFocusChangeListener() {
@Override
publicvoidonGlobalFocusChanged(View oldFocus, View newFocus){
if (newFocus != null) {
// newFocus.bringToFront();
mMoveView.setDrawUpRectEnabled(true);//设置居于放大的view之上。
float scale = 1.1f;
mMoveView.setFocusView(newFocus, mOldFocus, scale);
mMoveView.bringToFront();//将mMoveView的位置bringToFront()
mOldFocus = newFocus;//自己将移动后的View进行保存,
}
}
});
}
} |
|
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系
[邮箱地址] 删除
|