編輯:關於Android編程
IOS相比於Android,動畫效果是一方面優勢,IOS相機切換時滑動的動畫很不錯,看著是有一個3D的效果,而且變化感覺很自然。Android也可以通過Graphics下面的Camera可以實現3D效果,開始嘗試著用這個做了一下,效果不理想,滑動之後各組文字之間的距離就變了,從立體空間來說這是合邏輯的,但是看著很別捏。IOS相機的滑動效果文字之間的間隔在滑動的時候是不變的。
後面通過調整TextView X方向的scale使文字看著緊湊一點,然後通過計算的距離的方式,在滑動的時候保持各組文字之間的間隔一致,最後實現的效果還是和IOS的有一定的差距。先上個效果圖的。

下面逐步來說下怎麼實現:
MainaActivity.java:
往自定義的控件加了6個TextView,對應各個模式。
這裡面還實現了一個手勢監聽,來識別滑動事件。對動畫做了一些限制,角度小於30度,滑動距離大於15才能生效。
1 package com.example.androidcustomnview;
2
3 import android.app.Activity;
4 import android.graphics.Color;
5 import android.os.Bundle;
6 import android.util.Log;
7 import android.view.GestureDetector;
8 import android.view.GestureDetector.OnGestureListener;
9 import android.view.View;
10 import android.view.View.OnTouchListener;
11 import android.view.MotionEvent;
12 import android.view.TextureView;
13 import android.view.ViewGroup;
14 import android.view.animation.Animation;
15 import android.view.animation.Animation.AnimationListener;
16 import android.view.animation.AnimationSet;
17 import android.view.animation.TranslateAnimation;
18 import android.widget.FrameLayout;
19 import android.widget.LinearLayout;
20 import android.widget.RelativeLayout;
21 import android.widget.TextView;
22
23 public class MainActivity extends Activity implements OnTouchListener{
24
25 private static final String TAG = "MainActivity.TAG";
26 CustomViewL mCustomViewL;
27 String[] name = new String[] {"延時攝影","慢動作","視頻","拍照","正方形","全景"};
28
29 GestureDetector mGestureDetector;
30 RelativeLayout rootView;
31 @Override
32 protected void onCreate(Bundle savedInstanceState) {
33 super.onCreate(savedInstanceState);
34 setContentView(R.layout.activity_main);
35 mCustomViewL = (CustomViewL) findViewById(R.id.mCustomView);
36 rootView = (RelativeLayout) findViewById(R.id.ViewRoot);
37 rootView.setOnTouchListener(this);
38 mCustomViewL.getParent();
39 mCustomViewL.addIndicator(name);
40 mGestureDetector = new GestureDetector(this, new myGestureDetectorLis()); 48 }
49
50 class myGestureDetectorLis implements GestureDetector.OnGestureListener {
51
52 private static final int degreeLimit = 30;
53 private static final int distanceLimit = 15;
54
55 private boolean isScroll = false;
56 @Override
57 public boolean onDown(MotionEvent e) {
58 // TODO Auto-generated method stub
59 Log.d(TAG, "myGestureDetectorLis onDown");
60 isScroll = false;
61 return true;
62 }
63 @Override
64 public void onShowPress(MotionEvent e) {
65 // TODO Auto-generated method stub
66
67 }
68 @Override
69 public boolean onSingleTapUp(MotionEvent e) {
70 // TODO Auto-generated method stub
71 return false;
72 }
73
74 @Override
75 public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
76 float distanceY) {
77 // TODO Auto-generated method stub
78 if (isScroll) return false;
79 double degree = Math.atan(Math.abs(e2.getY() - e1.getY()) / Math.abs(e2.getX() - e1.getX())) * 180 /Math.PI;
80 float delta = e2.getX() - e1.getX();
81 if (delta > distanceLimit && degree < degreeLimit) {
82 Log.d(TAG, "向右滑");
83 isScroll = true;
84 mCustomViewL.scrollRight();
85 } else if (delta < -distanceLimit && degree < degreeLimit) {
86 Log.d(TAG, "向左滑");
87 isScroll = true;
88 mCustomViewL.scrollLeft();
89 }
90 return false;
91 }
92
93 @Override
94 public void onLongPress(MotionEvent e) {
95 // TODO Auto-generated method stub
96
97 }
98 @Override
99 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
100 float velocityY) {
101 // TODO Auto-generated method stub
102 return false;
103 }
104
105 }
106
107 @Override
108 public boolean onTouch(View v, MotionEvent event) {
109 // TODO Auto-generated method stub
110 return mGestureDetector.onTouchEvent(event);
111 }
112
113
114 }
CustomViewL.java:
自定義的控件,繼承自LinearLayout。在onLayout裡面,重新計算了下各個子控件的位置,因為各組文字的scale是不一樣的,必須重新Layout一下各個子控件的位置,是文字的顯示區域和點擊區域是一樣的,這樣給各個子控件設置的onClick事件才有效。
dispatchDraw方法是重繪各個子控件,更具各個子控件到中心控件的位置的距離,設置了各個TextView X方向的scale,為了就是看著要有一個立體的效果。
滑動之後,開始一個動畫,動畫結束之後重新requestLayout一下,重新計算下各個控件的位置。這個可以連續滑動的,如果這次動畫在執行,會保存一下,等動畫完了之後會接著跑下一個動畫。各個子控件滑動距離的計算有興趣的可以自己研究下,這裡就不贅述了,其實也是數學知識。
1 package com.example.androidcustomnview;
2
3 import android.content.Context;
4 import android.graphics.Camera;
5 import android.graphics.Canvas;
6 import android.graphics.Color;
7 import android.graphics.LinearGradient;
8 import android.graphics.Matrix;
9 import android.graphics.Paint;
10 import android.graphics.Shader;
11 import android.os.Handler;
12 import android.os.Message;
13 import android.util.AttributeSet;
14 import android.util.Log;
15 import android.view.MotionEvent;
16 import android.view.View;
17 import android.view.WindowManager;
18 import android.view.animation.Animation;
19 import android.view.animation.Animation.AnimationListener;
20 import android.view.animation.TranslateAnimation;
21 import android.widget.LinearLayout;
22 import android.widget.TextView;
23
24 public class CustomViewL extends LinearLayout {
25
26 private static final String TAG = "CustomViewL.TAG";
27 private Matrix mMatrix;
28 Camera mCamera;
29 private int mCurrentItem = 2;
30 private int screenWidth;
31 private Paint mPaint;
32
33 public static final float ItemScale = 0.1f;
34
35 public CustomViewL(Context context) {
36 super(context);
37 // TODO Auto-generated constructor stub
38 initView(context);
39 }
40
41 public CustomViewL(Context context, AttributeSet attrs, int defStyleAttr,
42 int defStyleRes) {
43 super(context, attrs, defStyleAttr, defStyleRes);
44 initView(context);
45 }
46
47 public CustomViewL(Context context, AttributeSet attrs, int defStyleAttr) {
48 super(context, attrs, defStyleAttr);
49 initView(context);
50 }
51
52 public CustomViewL(Context context, AttributeSet attrs) {
53 super(context, attrs);
54 initView(context);
55 }
56
57 private void initView(Context context) {
60 screenWidth = ((WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE))
61 .getDefaultDisplay().getWidth();
63 }
64
65 @Override
66 protected void onLayout(boolean changed, int l, int t, int r, int b) {
67 Log.d(TAG, "onLayout ");
68 super.onLayout(changed, l , t, r, b);
69 View v = getChildAt(mCurrentItem);
70 int delta = getWidth() / 2 - v.getLeft() - v.getWidth()/2;
71
72 for (int i = 0; i < getChildCount(); i++) {
73 View v1 = getChildAt(i);
74 if (i == mCurrentItem) {
75 v1.layout(v1.getLeft() + delta, v1.getTop(),
76 v1.getRight() + delta, v1.getBottom());
77 continue;
78 }
79 float mScale = Math.abs(i - mCurrentItem) * ItemScale;
80 int move = (int)(v1.getWidth() * mScale / 2);
81 if (i < mCurrentItem) {
82 for (int j = i + 1; j < mCurrentItem; j++) {
83 View v2 = getChildAt(j);
84 move += (int) (v2.getWidth() * Math.abs(j - mCurrentItem) * ItemScale);
85 }
86 } else {
87 for (int j = i - 1; j > mCurrentItem; j--) {
88 View v2 = getChildAt(j);
89 move += (int)(v2.getWidth() * Math.abs(j - mCurrentItem) * ItemScale);
90 }
91 move = -move;
92 }
93 v1.layout(v1.getLeft() + delta + move, v1.getTop(),
94 v1.getRight() + delta + move, v1.getBottom());
95 }
96 mRequstLayout = false;
97 }
98
99 @Override
100 protected void dispatchDraw(Canvas canvas) {
101 int count = getChildCount();
102 for (int i = 0; i < count; i++) {
103 updateChildItem(canvas,i);
104 }
105 }
106
107 public void updateChildItem(Canvas canvas,int item) {
108 // Log.d(TAG, "updateChildItem");
110 View v = getChildAt(item);
111 float desi = 1- Math.abs(item - mCurrentItem) * ItemScale;
112 ((TextView)v).setScaleX(desi);
113 drawChild(canvas, v, getDrawingTime());
114 updateTextColor();
115 }
118 private void updateTextColor() {
119 for (int i =0 ; i < getChildCount(); i++) {
120 if (i == mCurrentItem) {
121 ((TextView)getChildAt(i)).setTextColor(Color.YELLOW);
122 } else {
123 ((TextView)getChildAt(i)).setTextColor(Color.WHITE);
124 }
125 }
126 }
128 boolean scroolToRight = false;
129
130 public void scrollRight() {
131 if (mRequstLayout) return;
132 if (mCurrentItem > 0) {
133 if (mAnimationRunning) {
134 if (AnimationRunningCount < 1) {
135 currentItemCopy = mCurrentItem - 1;
136 AnimationRunningCount++;
137 scroolToRight = true;
138 }
139 return;
140 }
141 mCurrentItem--;
142 startTraAnimation(mCurrentItem,mCurrentItem + 1);
143 updateTextColor();
144 }
145 }
146
147 private int currentItemCopy;
148 public void scrollLeft() {
149 if (mRequstLayout) return;
150 if (mCurrentItem < getChildCount() - 1) {
151 if (mAnimationRunning) {
152 if (AnimationRunningCount < 1) {
153 currentItemCopy = mCurrentItem + 1;
154 AnimationRunningCount++;
155 scroolToRight = false;
156 }
157 return;
158 }
159 mCurrentItem++;
160 startTraAnimation(mCurrentItem,mCurrentItem-1);
161 updateTextColor();
162 }
163 }
164
165 public void addIndicator(String[] name) {
166 for (int i=0; i< name.length; i++) {
167 TextView mTextView = new TextView(getContext());
168 mTextView.setText(name[i]);
169 mTextView.setTextColor(Color.WHITE);
170 mTextView.setLines(1);
171 LinearLayout.LayoutParams ll = new LinearLayout.LayoutParams(
172 LinearLayout.LayoutParams.WRAP_CONTENT,
173 LinearLayout.LayoutParams.WRAP_CONTENT);
174 ll.setMargins(20, 0, 20, 0);
175 addView(mTextView,ll);
176 }
177 }
178
179 class myAnimationListener implements android.view.animation.Animation.AnimationListener {
180
181 @Override
182 public void onAnimationStart(Animation animation) {
183 Log.d(TAG, "onAnimationStart ");
184 mAnimationRunning = true;
185 }
186 @Override
187 public void onAnimationEnd(Animation animation) {
188 // TODO Auto-generated method stub
189 Log.d(TAG, "onAnimationEnd ");
190
191 for (int i= 0; i < getChildCount(); i++) {
192 getChildAt(i).clearAnimation();
193 }
194 mRequstLayout = true;
195 requestLayout();
196 mAnimationRunning = false;
197 if (AnimationRunningCount > 0) {
198 CustomViewL.this.post(new Runnable() {
199 @Override
200 public void run() {
201 // TODO Auto-generated method stub
202 AnimationRunningCount--;
203 mCurrentItem = currentItemCopy;
204 int lastItem = scroolToRight ? currentItemCopy + 1 : currentItemCopy - 1;
205 startTraAnimation(currentItemCopy,lastItem);
206 updateTextColor();
207 }
208 });
209 }
210 }
211 @Override
212 public void onAnimationRepeat(Animation animation) {
213 }
214
215 }
216
217 private int AnimitionDurationTime = 300;
218 private int AnimationRunningCount = 0;
219 private boolean mAnimationRunning = false;
220 private boolean mRequstLayout = false;
221 public void startTraAnimation(int item,int last) {
222 Log.d(TAG, "startTraAnimation item = " + item);
223 View v = getChildAt(item);
224 final int width = v.getWidth();
225 final int childCount = getChildCount();
226 int traslate = getWidth()/2 - v.getLeft() - width/2;
227
228 int currentItemWidthScale = (int) (width * ItemScale);
229
230 for (int i = 0; i < childCount; i++) {
231 int delta = currentItemWidthScale / 2;
233 Log.d(TAG, " i = " + i + " delta before = " + delta);
234 if (i < item) {
235 delta = -delta;
236 for (int j = i; j < item; j++) {
237 int a;
238 if (i == j) {
239 a = (int)(getChildAt(j).getWidth() * ItemScale / 2);
240 } else {
241 a = (int)(getChildAt(j).getWidth() * ItemScale);
242 }
243 delta = item < last ? delta - a : delta + a;
244 }
245 } else if (i > item){
246 for (int j = item + 1; j <= i; j++) {
247 int a;
248 if (j == i) {
249 a = (int)(getChildAt(j).getWidth() * ItemScale / 2);
250 } else {
251 a = (int)(getChildAt(j).getWidth() * ItemScale);
252 }
253 delta = item < last ? delta - a : delta + a;
254 }
255 } else {
256 delta = 0;
257 }
258 Log.d(TAG, "delta = " + delta);
259 delta += traslate;
260 TranslateAnimation translateAni = new TranslateAnimation(0, delta, 0, 0);
261 translateAni.setDuration(AnimitionDurationTime);
262 translateAni.setFillAfter(true);
263 if (i == item) translateAni.setAnimationListener(new myAnimationListener());
264 mAnimationRunning = true;
265 getChildAt(i).startAnimation(translateAni);
266 }
268 }
269 }
最後說一下布局文件,兩邊本來是要做一個陰影效果的,為了簡便,復習了下PS,就在上面蓋了張圖片,顯得兩邊有陰影。
整個來說其實也不復雜,有好些數學計算,幾何問題,效果也沒達到iphone的效果,如果有大神有想法,可以指導下。
AndroidStudio Git的使用(主要是解決文件沖突)
開始:1.使用github 來測試。首先准備一個GitHub賬號吧2.在androidStudio 裡面新建一個項目 這裡我取名TestGit 然後
Android——ListView控件
本篇介紹ListView控件,這是Android中比較重要也比較復雜的控件,這裡只談到使用ViewHolder機制優化即可。一、ListView簡介ListView是An
android 實現自定義組件
android開發中,對於復用率較高的多個控件,采用組件的方式可能更加方便,首先定義一個xml文件: 文件名:lyt_customer_service_phone.xml
Android控件系列之Toast使用介紹
Toast英文含義是吐司,在Android中,它就像烘烤機裡做好的吐司彈出來,並持續一小段時間後慢慢消失Toast也是一個容器,可以包含各種View,並承載著它們顯示。使