Android(安卓)自定义3D效果View
16lz
2022-07-24
由于工作需要,今天就使用Camera与Matrix实现3D效果
案例图:
1.自定义View
import android.animation.Animator;import android.animation.ValueAnimator;import android.content.Context;import android.graphics.Camera;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Matrix;import android.util.AttributeSet;import android.util.DisplayMetrics;import android.util.Log;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;public class ThreeDLayout extends ViewGroup { private Camera mCamera; private Matrix mMatrix; //this viewgroup's center private int mCenterX; private int mCenterY; //rotateDegree private float mCanvasRotateY; private float mCanvasRotateX; private float mCanvasMaxRotateDegree = 50; //the touch mode public static int MODE_X = 0; public static int MODE_Y = 1; public static int MODE_BOTH_X_Y = 2; private int mMode = MODE_BOTH_X_Y; private float mDensity; private float[] mValues = new float[9]; //the flag of touch private boolean isCanTouch = false; //the degree of animation private float mDegreeY = 0; private float mDegreeX = 0; //the flag of animate private boolean isPlaying = false; //the degree of longer animate private int mLoopAnimateY = 0; public ThreeDLayout(Context context) { this(context, null); } public ThreeDLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ThreeDLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); //set a default background to make sure onDraw() dispatch if (getBackground() == null) { setBackgroundColor(Color.parseColor("#ffffff")); } DisplayMetrics dm = new DisplayMetrics(); dm = getResources().getDisplayMetrics(); mDensity = dm.density; mCamera = new Camera(); mMatrix = new Matrix(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (getChildCount() != 1) { throw new IllegalStateException("ThreeDLayout can only have one child"); } View child = getChildAt(0); measureChild(child, widthMeasureSpec, heightMeasureSpec); //only one child view,so give the same size setMeasuredDimension(child.getMeasuredWidth(), child.getMeasuredHeight()); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { View child = getChildAt(0); child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight()); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mCenterX = w / 2; mCenterY = h / 2; } @Override protected void onDraw(Canvas canvas) { mMatrix.reset(); mCamera.save(); if (mMode == MODE_Y || mMode == MODE_BOTH_X_Y) { mCamera.rotateX(mCanvasRotateX); } if (mMode == MODE_X || mMode == MODE_BOTH_X_Y) { mCamera.rotateY(mCanvasRotateY); } mCamera.rotateY(mDegreeY); mCamera.rotateX(mDegreeX); if (isPlaying) { mCamera.rotateY(mLoopAnimateY++); Log.e("wing", mLoopAnimateY + ""); if (mLoopAnimateY == 360) { mLoopAnimateY = 0; } invalidate(); } mCamera.getMatrix(mMatrix); // fix the Camera bug, mMatrix.getValues(mValues); mValues[6] = mValues[6] / mDensity; mValues[7] = mValues[7] / mDensity; mMatrix.setValues(mValues); mCamera.restore(); mMatrix.preTranslate(-mCenterX, -mCenterY); mMatrix.postTranslate(mCenterX, mCenterY); canvas.concat(mMatrix); super.onDraw(canvas); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (isCanTouch) { return true; } else { return super.onInterceptTouchEvent(ev); } } @Override public boolean onTouchEvent(MotionEvent event) { if (isCanTouch) { float x = event.getX(); float y = event.getY(); int action = event.getAction(); switch (action) { case MotionEvent.ACTION_MOVE: { rotateCanvasWhenMove(x, y); invalidate(); return true; } case MotionEvent.ACTION_UP: { mDegreeY = 0; rotateCanvasWhenMove(mCenterX, mCenterY); invalidate(); return true; } } return true; } else { return super.onTouchEvent(event); } } /** * get the value to rotate */ private void rotateCanvasWhenMove(float x, float y) { float dx = x - mCenterX; float dy = y - mCenterY; float percentX = dx / mCenterX; float percentY = dy / mCenterY; if (percentX > 1f) { percentX = 1f; } else if (percentX < -1f) { percentX = -1f; } if (percentY > 1f) { percentY = 1f; } else if (percentY < -1f) { percentY = -1f; } mCanvasRotateY = mCanvasMaxRotateDegree * percentX; mCanvasRotateX = -(mCanvasMaxRotateDegree * percentY); } public void setTouchable(boolean canTouch) { isCanTouch = canTouch; } public void setTouchMode(int mode) { mMode = mode; isCanTouch = true; } /** * set the max rotate degree */ public void setMaxRotateDegree(int degree) { mCanvasMaxRotateDegree = degree; } /** * start horizontal turn animate */ public void startHorizontalAnimate(long duration) { final ValueAnimator animator = ValueAnimator.ofFloat(-180f, 0f); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mDegreeY = (float) animation.getAnimatedValue(); invalidate(); } }); animator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { mDegreeY = 0; animator.removeAllUpdateListeners(); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); animator.setDuration(duration); animator.start(); } /** * start horizontal turn animate delayed */ public void startHorizontalAnimateDelayed(final long delayed, final long duration) { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(delayed); } catch (InterruptedException e) { e.printStackTrace(); } post(new Runnable() { @Override public void run() { startHorizontalAnimate(duration); } }); } }).start(); } /** * start vertical turn animate */ public void startVerticalAnimate(long duration) { final ValueAnimator animator = ValueAnimator.ofFloat(-180f, 0f); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mDegreeX = (float) animation.getAnimatedValue(); invalidate(); } }); animator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { mDegreeX = 0; animator.removeAllUpdateListeners(); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); animator.setDuration(duration); animator.start(); } /** * start vertical turn animate delayed */ public void startVerticalAnimateDelayed(final long delayed, final long duration) { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(delayed); } catch (InterruptedException e) { e.printStackTrace(); } post(new Runnable() { @Override public void run() { startVerticalAnimate(duration); } }); } }).start(); } /** * start loop animate */ public void startHorizontalAnimate() { isPlaying = true; invalidate(); } /** * stop the loop animate */ public void stopAnimate() { isPlaying = false; mLoopAnimateY = 0; invalidate(); }}
2.Activity中使用
import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.support.v7.widget.LinearLayoutManager;import android.support.v7.widget.RecyclerView;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.TextView;import com.wingsofts.threedlayout.ThreeDLayout;import java.util.ArrayList;import java.util.List;public class WeatherActivity extends AppCompatActivity { RecyclerView recyclerView; private Adapter adapter; private boolean flag = true; List<String> list = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_weather); recyclerView = (RecyclerView) findViewById(R.id.recyclerView); list.add("周日"); list.add("周一"); list.add("周二"); list.add("周三"); list.add("周四"); list.add("周五"); list.add("周六"); adapter = new Adapter(list); recyclerView.setAdapter(adapter); recyclerView.setLayoutManager(new LinearLayoutManager(this)); ThreeDLayout layout = (ThreeDLayout) findViewById(R.id.td_header); layout.setTouchable(true); layout.setTouchMode(ThreeDLayout.MODE_BOTH_X_Y); } public void onClick(View v){ ThreeDLayout layout = (ThreeDLayout) findViewById(R.id.td_header); if(!flag){ flag = true; ((TextView)findViewById(R.id.textView)).setText("72℉"); }else { flag = false; ((TextView)findViewById(R.id.textView)).setText("30℃"); } layout.startHorizontalAnimate(1000); for(int i = 0;i<list.size();i++){ ((ThreeDLayout)recyclerView.getChildAt(i)).startHorizontalAnimateDelayed(100*i,1000); } } class Adapter extends RecyclerView.Adapter<Adapter.MyViewHolder> { List<String> list; public Adapter(List<String> list) { this.list = list; } @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return new MyViewHolder( LayoutInflater.from(parent.getContext()).inflate(R.layout.item_main, parent, false)); } @Override public void onBindViewHolder(Adapter.MyViewHolder holder, int position) { holder.textView.setText(list.get(position)); } @Override public int getItemCount() { return list.size(); } class MyViewHolder extends RecyclerView.ViewHolder { TextView textView; TextView temperatureTxt; MyViewHolder(View itemView) { super(itemView); textView = (TextView) itemView.findViewById(R.id.textView); temperatureTxt = (TextView) itemView.findViewById(R.id.tv_temperature); } } }}
3.activity_weather.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/threeDLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.wingsofts.myapplication.WeatherActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.wingsofts.threedlayout.ThreeDLayout android:id="@+id/td_header" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/colorPrimary"> <TextView android:id="@+id/textView" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:text="30℃" android:textColor="#fff" android:textSize="80sp" /> </com.wingsofts.threedlayout.ThreeDLayout> <com.wingsofts.myapplication.MyRecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="wrap_content" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="onClick" android:text="START" /> </LinearLayout></LinearLayout>
item_main.xml
<?xml version="1.0" encoding="utf-8"?><com.wingsofts.threedlayout.ThreeDLayout xmlns:android="http://schemas.android.com/apk/res/android" android:background="@color/colorPrimary" android:layout_width="match_parent" android:layout_height="wrap_content" > <LinearLayout android:background="@color/colorPrimary" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="16dp" > <TextView android:id="@+id/textView" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="left" android:textColor="#fff" android:textSize="16sp" /> <TextView android:id="@+id/tv_temperature" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="30℃" android:textColor="#fff" android:textSize="16sp" /> </LinearLayout></com.wingsofts.threedlayout.ThreeDLayout>
更多相关文章
- 箭头函数的基础使用
- python起点网月票榜字体反爬案例
- NPM 和webpack 的基础使用
- Python list sort方法的具体使用
- 【阿里云镜像】使用阿里巴巴DNS镜像源——DNS配置教程
- Android(安卓)Activity界面切换添加动画特效
- android 使用html5作布局文件: webview跟javascript交互
- Android(安卓)Resource介绍和使用
- "Failed to fetch URL https://dl-ssl.google.com/android/repos