Android實(shí)現(xiàn)IOS相機(jī)滑動控件
IOS相比于Android,動畫效果是一方面優(yōu)勢,IOS相機(jī)切換時滑動的動畫很不錯,看著是有一個3D的效果,而且變化感覺很自然。Android也可以通過Graphics下面的Camera可以實(shí)現(xiàn)3D效果,開始嘗試著用這個做了一下,效果不理想,滑動之后各組文字之間的距離就變了,從立體空間來說這是合邏輯的,但是看著很別捏。IOS相機(jī)的滑動效果文字之間的間隔在滑動的時候是不變的。
后面通過調(diào)整TextView X方向的scale使文字看著緊湊一點(diǎn),然后通過計算的距離的方式,在滑動的時候保持各組文字之間的間隔一致,最后實(shí)現(xiàn)的效果還是和IOS的有一定的差距。先上個效果圖的。
下面逐步來說下怎么實(shí)現(xiàn):
MainaActivity.java:
往自定義的控件加了6個TextView,對應(yīng)各個模式。
這里面還實(shí)現(xiàn)了一個手勢監(jiān)聽,來識別滑動事件。對動畫做了一些限制,角度小于30度,滑動距離大于15才能生效。
package com.example.androidcustomnview; import android.app.Activity; import android.graphics.Color; import android.os.Bundle; import android.util.Log; import android.view.GestureDetector; import android.view.GestureDetector.OnGestureListener; import android.view.View; import android.view.View.OnTouchListener; import android.view.MotionEvent; import android.view.TextureView; import android.view.ViewGroup; import android.view.animation.Animation; import android.view.animation.Animation.AnimationListener; import android.view.animation.AnimationSet; import android.view.animation.TranslateAnimation; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; public class MainActivity extends Activity implements OnTouchListener{ private static final String TAG = "MainActivity.TAG"; CustomViewL mCustomViewL; String[] name = new String[] {"延時攝影","慢動作","視頻","拍照","正方形","全景"}; GestureDetector mGestureDetector; RelativeLayout rootView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mCustomViewL = (CustomViewL) findViewById(R.id.mCustomView); rootView = (RelativeLayout) findViewById(R.id.ViewRoot); rootView.setOnTouchListener(this); mCustomViewL.getParent(); mCustomViewL.addIndicator(name); mGestureDetector = new GestureDetector(this, new myGestureDetectorLis()); 48 } class myGestureDetectorLis implements GestureDetector.OnGestureListener { private static final int degreeLimit = 30; private static final int distanceLimit = 15; private boolean isScroll = false; @Override public boolean onDown(MotionEvent e) { // TODO Auto-generated method stub Log.d(TAG, "myGestureDetectorLis onDown"); isScroll = false; return true; } @Override public void onShowPress(MotionEvent e) { // TODO Auto-generated method stub } @Override public boolean onSingleTapUp(MotionEvent e) { // TODO Auto-generated method stub return false; } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { // TODO Auto-generated method stub if (isScroll) return false; double degree = Math.atan(Math.abs(e2.getY() - e1.getY()) / Math.abs(e2.getX() - e1.getX())) * 180 /Math.PI; float delta = e2.getX() - e1.getX(); if (delta > distanceLimit && degree < degreeLimit) { Log.d(TAG, "向右滑"); isScroll = true; mCustomViewL.scrollRight(); } else if (delta < -distanceLimit && degree < degreeLimit) { Log.d(TAG, "向左滑"); isScroll = true; mCustomViewL.scrollLeft(); } return false; } @Override public void onLongPress(MotionEvent e) { // TODO Auto-generated method stub } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { // TODO Auto-generated method stub return false; } } @Override public boolean onTouch(View v, MotionEvent event) { // TODO Auto-generated method stub return mGestureDetector.onTouchEvent(event); } }
CustomViewL.java:
自定義的控件,繼承自LinearLayout。在onLayout里面,重新計算了下各個子控件的位置,因?yàn)楦鹘M文字的scale是不一樣的,必須重新Layout一下各個子控件的位置,是文字的顯示區(qū)域和點(diǎn)擊區(qū)域是一樣的,這樣給各個子控件設(shè)置的onClick事件才有效。
dispatchDraw方法是重繪各個子控件,更具各個子控件到中心控件的位置的距離,設(shè)置了各個TextView X方向的scale,為了就是看著要有一個立體的效果。
滑動之后,開始一個動畫,動畫結(jié)束之后重新requestLayout一下,重新計算下各個控件的位置。這個可以連續(xù)滑動的,如果這次動畫在執(zhí)行,會保存一下,等動畫完了之后會接著跑下一個動畫。各個子控件滑動距離的計算有興趣的可以自己研究下,這里就不贅述了,其實(shí)也是數(shù)學(xué)知識。
package com.example.androidcustomnview; import android.content.Context; import android.graphics.Camera; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.LinearGradient; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Shader; import android.os.Handler; import android.os.Message; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; import android.view.animation.Animation; import android.view.animation.Animation.AnimationListener; import android.view.animation.TranslateAnimation; import android.widget.LinearLayout; import android.widget.TextView; public class CustomViewL extends LinearLayout { private static final String TAG = "CustomViewL.TAG"; private Matrix mMatrix; Camera mCamera; private int mCurrentItem = 2; private int screenWidth; private Paint mPaint; public static final float ItemScale = 0.1f; public CustomViewL(Context context) { super(context); // TODO Auto-generated constructor stub initView(context); } public CustomViewL(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); initView(context); } public CustomViewL(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(context); } public CustomViewL(Context context, AttributeSet attrs) { super(context, attrs); initView(context); } private void initView(Context context) { screenWidth = ((WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE)) .getDefaultDisplay().getWidth(); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { Log.d(TAG, "onLayout "); super.onLayout(changed, l , t, r, b); View v = getChildAt(mCurrentItem); int delta = getWidth() / 2 - v.getLeft() - v.getWidth()/2; for (int i = 0; i < getChildCount(); i++) { View v1 = getChildAt(i); if (i == mCurrentItem) { v1.layout(v1.getLeft() + delta, v1.getTop(), v1.getRight() + delta, v1.getBottom()); continue; } float mScale = Math.abs(i - mCurrentItem) * ItemScale; int move = (int)(v1.getWidth() * mScale / 2); if (i < mCurrentItem) { for (int j = i + 1; j < mCurrentItem; j++) { View v2 = getChildAt(j); move += (int) (v2.getWidth() * Math.abs(j - mCurrentItem) * ItemScale); } } else { for (int j = i - 1; j > mCurrentItem; j--) { View v2 = getChildAt(j); move += (int)(v2.getWidth() * Math.abs(j - mCurrentItem) * ItemScale); } move = -move; } v1.layout(v1.getLeft() + delta + move, v1.getTop(), v1.getRight() + delta + move, v1.getBottom()); } mRequstLayout = false; } @Override protected void dispatchDraw(Canvas canvas) { int count = getChildCount(); for (int i = 0; i < count; i++) { updateChildItem(canvas,i); } } public void updateChildItem(Canvas canvas,int item) { // Log.d(TAG, "updateChildItem"); View v = getChildAt(item); float desi = 1- Math.abs(item - mCurrentItem) * ItemScale; ((TextView)v).setScaleX(desi); drawChild(canvas, v, getDrawingTime()); updateTextColor(); } private void updateTextColor() { for (int i =0 ; i < getChildCount(); i++) { if (i == mCurrentItem) { ((TextView)getChildAt(i)).setTextColor(Color.YELLOW); } else { ((TextView)getChildAt(i)).setTextColor(Color.WHITE); } } } boolean scroolToRight = false; public void scrollRight() { if (mRequstLayout) return; if (mCurrentItem > 0) { if (mAnimationRunning) { if (AnimationRunningCount < 1) { currentItemCopy = mCurrentItem - 1; AnimationRunningCount++; scroolToRight = true; } return; } mCurrentItem--; startTraAnimation(mCurrentItem,mCurrentItem + 1); updateTextColor(); } } private int currentItemCopy; public void scrollLeft() { if (mRequstLayout) return; if (mCurrentItem < getChildCount() - 1) { if (mAnimationRunning) { if (AnimationRunningCount < 1) { currentItemCopy = mCurrentItem + 1; AnimationRunningCount++; scroolToRight = false; } return; } mCurrentItem++; startTraAnimation(mCurrentItem,mCurrentItem-1); updateTextColor(); } } public void addIndicator(String[] name) { for (int i=0; i< name.length; i++) { TextView mTextView = new TextView(getContext()); mTextView.setText(name[i]); mTextView.setTextColor(Color.WHITE); mTextView.setLines(1); LinearLayout.LayoutParams ll = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); ll.setMargins(20, 0, 20, 0); addView(mTextView,ll); } } class myAnimationListener implements android.view.animation.Animation.AnimationListener { @Override public void onAnimationStart(Animation animation) { Log.d(TAG, "onAnimationStart "); mAnimationRunning = true; } @Override public void onAnimationEnd(Animation animation) { // TODO Auto-generated method stub Log.d(TAG, "onAnimationEnd "); for (int i= 0; i < getChildCount(); i++) { getChildAt(i).clearAnimation(); } mRequstLayout = true; requestLayout(); mAnimationRunning = false; if (AnimationRunningCount > 0) { CustomViewL.this.post(new Runnable() { @Override public void run() { // TODO Auto-generated method stub AnimationRunningCount--; mCurrentItem = currentItemCopy; int lastItem = scroolToRight ? currentItemCopy + 1 : currentItemCopy - 1; startTraAnimation(currentItemCopy,lastItem); updateTextColor(); } }); } } @Override public void onAnimationRepeat(Animation animation) { } } private int AnimitionDurationTime = 300; private int AnimationRunningCount = 0; private boolean mAnimationRunning = false; private boolean mRequstLayout = false; public void startTraAnimation(int item,int last) { Log.d(TAG, "startTraAnimation item = " + item); View v = getChildAt(item); final int width = v.getWidth(); final int childCount = getChildCount(); int traslate = getWidth()/2 - v.getLeft() - width/2; int currentItemWidthScale = (int) (width * ItemScale); for (int i = 0; i < childCount; i++) { int delta = currentItemWidthScale / 2; Log.d(TAG, " i = " + i + " delta before = " + delta); if (i < item) { delta = -delta; for (int j = i; j < item; j++) { int a; if (i == j) { a = (int)(getChildAt(j).getWidth() * ItemScale / 2); } else { a = (int)(getChildAt(j).getWidth() * ItemScale); } delta = item < last ? delta - a : delta + a; } } else if (i > item){ for (int j = item + 1; j <= i; j++) { int a; if (j == i) { a = (int)(getChildAt(j).getWidth() * ItemScale / 2); } else { a = (int)(getChildAt(j).getWidth() * ItemScale); } delta = item < last ? delta - a : delta + a; } } else { delta = 0; } Log.d(TAG, "delta = " + delta); delta += traslate; TranslateAnimation translateAni = new TranslateAnimation(0, delta, 0, 0); translateAni.setDuration(AnimitionDurationTime); translateAni.setFillAfter(true); if (i == item) translateAni.setAnimationListener(new myAnimationListener()); mAnimationRunning = true; getChildAt(i).startAnimation(translateAni); } } }
最后說一下布局文件,兩邊本來是要做一個陰影效果的,為了簡便,復(fù)習(xí)了下PS,就在上面蓋了張圖片,顯得兩邊有陰影。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.androidcustomnview.MainActivity" > <RelativeLayout android:id="@+id/ViewRoot" android:gravity="center" android:layout_width="match_parent" android:layout_height="match_parent"> <com.example.androidcustomnview.CustomViewL android:orientation="horizontal" android:background="@android:color/background_dark" android:id="@+id/mCustomView" android:layout_width="match_parent" android:layout_height="wrap_content" > </com.example.androidcustomnview.CustomViewL> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@id/mCustomView" android:layout_alignTop="@id/mCustomView" android:layout_alignRight="@id/mCustomView" android:layout_alignBottom="@id/mCustomView" android:background="@drawable/test"/> </RelativeLayout> </RelativeLayout>
整個來說其實(shí)也不復(fù)雜,有好些數(shù)學(xué)計算,幾何問題,效果也沒達(dá)到iphone的效果,如果有大神有想法,可以指導(dǎo)下。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- iOS自定義相機(jī)實(shí)現(xiàn)拍照、錄制視頻
- iOS框架AVFoundation實(shí)現(xiàn)相機(jī)拍照、錄制視頻
- iOS仿微信相機(jī)拍照、視頻錄制功能
- IOS打開照相機(jī)與本地相冊選擇圖片實(shí)例詳解
- iOS開發(fā)-自定義相機(jī)實(shí)例(仿微信)
- iOS開發(fā)-調(diào)用系統(tǒng)相機(jī)和相冊獲取照片示例
- IOS10 相冊相機(jī)閃退bug解決辦法
- iOS8調(diào)用相機(jī)報警告Snapshotting a view的解決方法
- iOS 10 使用相機(jī)相簿閃退的bug修正方法
- IOS打開系統(tǒng)相機(jī)的閃光燈
- iOS開發(fā)技巧之自定義相機(jī)
相關(guān)文章
Android開發(fā)中Intent.Action各種常見的作用匯總
今天小編就為大家分享一篇關(guān)于Android開發(fā)中Intent.Action各種常見的作用匯總,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-12-12Android實(shí)現(xiàn)圖片設(shè)置圓角形式
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)圖片設(shè)置圓角形式,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-11-11Android 中ListView和GridView賦值錯位
這篇文章主要介紹了Android 中ListView和GridView賦值錯位的相關(guān)資料,希望通過本文能幫助到大家,需要的朋友可以參考下2017-10-10Flutter如何通過一行命令解決多個pubspec.yaml文件的依賴項問題
這篇文章主要介紹了Flutter如何通過一行命令解決多個pubspec.yaml文件的依賴項問題,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-06-06Android Studio報錯Manifest merger failed with multiple errors
這篇文章主要介紹了Android Studio報錯Manifest merger failed with multiple errors2017-10-10Android通過json向MySQL中讀寫數(shù)據(jù)的方法詳解【讀取篇】
這篇文章主要介紹了Android通過json向MySQL中讀寫數(shù)據(jù)的方法,涉及Android解析json以及與php交互讀取mysql的方法,需要的朋友可以參考下2016-06-06Android代碼檢查規(guī)則Lint的自定義與應(yīng)用詳解
本文主要介紹了Android代碼檢查規(guī)則Lint的自定義與應(yīng)用詳解,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-04-04