Android實(shí)現(xiàn)IOS相機(jī)滑動(dòng)控件
IOS相比于Android,動(dòng)畫(huà)效果是一方面優(yōu)勢(shì),IOS相機(jī)切換時(shí)滑動(dòng)的動(dòng)畫(huà)很不錯(cuò),看著是有一個(gè)3D的效果,而且變化感覺(jué)很自然。Android也可以通過(guò)Graphics下面的Camera可以實(shí)現(xiàn)3D效果,開(kāi)始嘗試著用這個(gè)做了一下,效果不理想,滑動(dòng)之后各組文字之間的距離就變了,從立體空間來(lái)說(shuō)這是合邏輯的,但是看著很別捏。IOS相機(jī)的滑動(dòng)效果文字之間的間隔在滑動(dòng)的時(shí)候是不變的。
后面通過(guò)調(diào)整TextView X方向的scale使文字看著緊湊一點(diǎn),然后通過(guò)計(jì)算的距離的方式,在滑動(dòng)的時(shí)候保持各組文字之間的間隔一致,最后實(shí)現(xiàn)的效果還是和IOS的有一定的差距。先上個(gè)效果圖的。

下面逐步來(lái)說(shuō)下怎么實(shí)現(xiàn):
MainaActivity.java:
往自定義的控件加了6個(gè)TextView,對(duì)應(yīng)各個(gè)模式。
這里面還實(shí)現(xiàn)了一個(gè)手勢(shì)監(jiān)聽(tīng),來(lái)識(shí)別滑動(dòng)事件。對(duì)動(dòng)畫(huà)做了一些限制,角度小于30度,滑動(dòng)距離大于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[] {"延時(shí)攝影","慢動(dòng)作","視頻","拍照","正方形","全景"};
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里面,重新計(jì)算了下各個(gè)子控件的位置,因?yàn)楦鹘M文字的scale是不一樣的,必須重新Layout一下各個(gè)子控件的位置,是文字的顯示區(qū)域和點(diǎn)擊區(qū)域是一樣的,這樣給各個(gè)子控件設(shè)置的onClick事件才有效。
dispatchDraw方法是重繪各個(gè)子控件,更具各個(gè)子控件到中心控件的位置的距離,設(shè)置了各個(gè)TextView X方向的scale,為了就是看著要有一個(gè)立體的效果。
滑動(dòng)之后,開(kāi)始一個(gè)動(dòng)畫(huà),動(dòng)畫(huà)結(jié)束之后重新requestLayout一下,重新計(jì)算下各個(gè)控件的位置。這個(gè)可以連續(xù)滑動(dòng)的,如果這次動(dòng)畫(huà)在執(zhí)行,會(huì)保存一下,等動(dòng)畫(huà)完了之后會(huì)接著跑下一個(gè)動(dòng)畫(huà)。各個(gè)子控件滑動(dòng)距離的計(jì)算有興趣的可以自己研究下,這里就不贅述了,其實(shí)也是數(shù)學(xué)知識(shí)。
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);
}
}
}
最后說(shuō)一下布局文件,兩邊本來(lái)是要做一個(gè)陰影效果的,為了簡(jiǎn)便,復(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>
整個(gè)來(lái)說(shuō)其實(shí)也不復(fù)雜,有好些數(shù)學(xué)計(jì)算,幾何問(wèn)題,效果也沒(méi)達(dá)到iphone的效果,如果有大神有想法,可以指導(dǎo)下。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- iOS自定義相機(jī)實(shí)現(xiàn)拍照、錄制視頻
- iOS框架AVFoundation實(shí)現(xiàn)相機(jī)拍照、錄制視頻
- iOS仿微信相機(jī)拍照、視頻錄制功能
- IOS打開(kāi)照相機(jī)與本地相冊(cè)選擇圖片實(shí)例詳解
- iOS開(kāi)發(fā)-自定義相機(jī)實(shí)例(仿微信)
- iOS開(kāi)發(fā)-調(diào)用系統(tǒng)相機(jī)和相冊(cè)獲取照片示例
- IOS10 相冊(cè)相機(jī)閃退bug解決辦法
- iOS8調(diào)用相機(jī)報(bào)警告Snapshotting a view的解決方法
- iOS 10 使用相機(jī)相簿閃退的bug修正方法
- IOS打開(kāi)系統(tǒng)相機(jī)的閃光燈
- iOS開(kāi)發(fā)技巧之自定義相機(jī)
相關(guān)文章
Android 列表選擇框 Spinner詳解及實(shí)例
這篇文章主要介紹了Android 列表選擇框 Spinner詳解及實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-06-06
Android ViewPager實(shí)現(xiàn)頁(yè)面左右切換效果
這篇文章主要為大家詳細(xì)介紹了Android ViewPager實(shí)現(xiàn)頁(yè)面左右切換效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
Android應(yīng)用接入微信分享的實(shí)例代碼
本篇文章主要介紹了Android應(yīng)用接入微信分享的實(shí)例代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-07-07
Android 設(shè)置Edittext獲取焦點(diǎn)并彈出軟鍵盤(pán)
本文主要介紹了Android設(shè)置Edittext獲取焦點(diǎn)并彈出軟鍵盤(pán)的實(shí)現(xiàn)代碼。具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧2017-04-04
Android中Listview點(diǎn)贊功能的實(shí)現(xiàn)
最近一段時(shí)間在研究android方面的知識(shí),利用listview實(shí)現(xiàn)點(diǎn)贊功能,下面小編通過(guò)本文給大家介紹下基本思路,需要的朋友可以參考下2016-11-11
Android項(xiàng)目實(shí)戰(zhàn)之仿網(wǎng)易新聞的頁(yè)面(RecyclerView )
這篇文章主要介紹了Android項(xiàng)目實(shí)戰(zhàn)之仿網(wǎng)易新聞的頁(yè)面,ViewPager作為RecyclerView的Header,感興趣的小伙伴們可以參考一下2016-01-01
Kotlin StateFlow單數(shù)據(jù)更新熱流設(shè)計(jì)與使用介紹
StateFlow當(dāng)值發(fā)生變化,就會(huì)將值發(fā)送出去,下流就可以接收到新值。在某些場(chǎng)景下,StateFlow比LiveData更適用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2022-09-09
Flutter進(jìn)階之實(shí)現(xiàn)動(dòng)畫(huà)效果(十)
這篇文章主要為大家詳細(xì)介紹了Flutter進(jìn)階之實(shí)現(xiàn)動(dòng)畫(huà)效果的第十篇,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-08-08
Flutter 開(kāi)發(fā)一個(gè)登錄頁(yè)面
登錄頁(yè)面在 App 開(kāi)發(fā)中非常常見(jiàn),本篇借登錄頁(yè)面的開(kāi)發(fā)介紹了文本框 TextField組件的使用,同時(shí)使用文本框的裝飾屬性實(shí)現(xiàn)了個(gè)性化文本框設(shè)置。2021-06-06
Handler實(shí)現(xiàn)倒計(jì)時(shí)功能
這篇文章主要為大家詳細(xì)介紹了Handler實(shí)現(xiàn)倒計(jì)時(shí)功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-04-04

