首先我们还是看一些示例:(网易,新浪,百度)
显示效果都不错,可是手感就不一样了,百度最棒,网易还行,新浪就操作很不好,这里我说的是滑动切换图片.自己可以测试一下.不得不说牛叉的公司确实有哦牛叉的道理.
下面我简单的介绍下实现方法:其实就是listview addHeaderView.只不过这个view是一个可以切换图片的view,至于这个view怎么做,就要根据自己的喜爱了,实现有多种方法,下面我简单介绍一下.
第一种:ViewFlipper+GestureDetector
主布局就是一个listview,这里就不介绍了,我介绍下切换图片布局head_iamge.xml
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
<span style=
"font-size:12px;"
><?xml version=
"1.0"
encoding=
"utf-8"
?>
<LinearLayout xmlns:android=
"http://schemas.android.com/apk/res/android"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:orientation=
"vertical"
>
<FrameLayout
android:id=
"@+id/fl_main"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
>
<ViewFlipper
android:id=
"@+id/ViewFlipper01"
android:layout_width=
"fill_parent"
android:layout_height=
"fill_parent"
>
</ViewFlipper>
<LinearLayout
android:id=
"@+id/ll_point"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_gravity=
"bottom|center_horizontal"
android:layout_marginBottom=
"10dp"
android:src=
"@drawable/indicator"
/>
</FrameLayout>
</LinearLayout></span>
|
这里我就添加一系列切换点,至于显示新闻标题,透明效果等等,大家可以自己布局,方法同理,不难实现.
接下来我们看动画布局.
push_left_in.xml
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
<span style=
"font-size:12px;"
><?xml version=
"1.0"
encoding=
"utf-8"
?>
<set xmlns:android=
"http://schemas.android.com/apk/res/android"
android:fillAfter=
"true"
>
<translate
android:duration=
"500"
android:fromXDelta=
"-100%p"
android:toXDelta=
"0"
/>
<alpha
android:duration=
"500"
android:fromAlpha=
"0.1"
android:toAlpha=
"1.0"
/>
</set></span>
|
push_left_out.xml
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
<span style=
"font-size:12px;"
><?xml version=
"1.0"
encoding=
"utf-8"
?>
<translate
android:duration=
"500"
android:fromXDelta=
"0"
android:toXDelta=
"-100%p"
/>
<alpha
android:duration=
"500"
android:fromAlpha=
"1.0"
android:toAlpha=
"0.5"
/>
</set></span>
|
push_right_in.xml
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
<span style=
"font-size:12px;"
><?xml version=
"1.0"
encoding=
"utf-8"
?>
<set xmlns:android=
"http://schemas.android.com/apk/res/android"
android:fillAfter=
"true"
>
<translate
android:duration=
"500"
android:fromXDelta=
"100%p"
android:toXDelta=
"0"
/>
<alpha
android:duration=
"500"
android:fromAlpha=
"0.1"
android:toAlpha=
"1.0"
/>
</set></span>
|
push_right_out.xml
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
<span style=
"font-size:12px;"
><?xml version=
"1.0"
encoding=
"utf-8"
?>
<translate
android:duration=
"500"
android:fromXDelta=
"0"
android:toXDelta=
"100%p"
/>
<alpha
android:duration=
"500"
android:fromAlpha=
"1.0"
android:toAlpha=
"0.5"
/>
</set></span>
|
我简单介绍下这些布局:
push_left_in:左边进入,则要进入的view初始位置在-100%p位置,终止位置在0,而push_left_out:左边出来,则此时view的位置在0,而终止位置在-100%p.
右进右出同理,至于alpha渐变,很简单,动画就说道这里,相信了解动画的同学们不用看就ok了.
下面重点是如何实现.
代码:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
|
<span style=
"font-size:12px;"
>
package
com.jj.chage2;
import
java.util.ArrayList;
import
java.util.Timer;
import
java.util.TimerTask;
import
android.app.Activity;
import
android.content.Context;
import
android.graphics.Bitmap;
import
android.graphics.BitmapFactory;
import
android.graphics.Matrix;
import
android.os.Bundle;
import
android.util.Log;
import
android.view.GestureDetector;
import
android.view.LayoutInflater;
import
android.view.MotionEvent;
import
android.view.View;
import
android.view.ViewGroup;
import
android.view.View.OnTouchListener;
import
android.view.animation.AnimationUtils;
import
android.widget.AdapterView;
import
android.widget.AdapterView.OnItemClickListener;
import
android.widget.Button;
import
android.widget.ImageView.ScaleType;
import
android.widget.LinearLayout.LayoutParams;
import
android.widget.ArrayAdapter;
import
android.widget.FrameLayout;
import
android.widget.ImageView;
import
android.widget.LinearLayout;
import
android.widget.ListView;
import
android.widget.TextView;
import
android.widget.Toast;
import
android.widget.ViewFlipper;
public
class
MainActivity
extends
Activity
implements
GestureDetector.OnGestureListener {
private
GestureDetector detector;
private
ViewFlipper flipper;
private
int
image_id[] = { R.drawable.a, R.drawable.b, R.drawable.c };
private
ListView lv_main;
private
LayoutInflater layoutInflater;
private
LinearLayout ll_point;
private
FrameLayout frameLayout;
private
final
String msg[] = {
"one"
,
"two"
,
"three"
,
"four"
,
"five"
,
"six"
,
"seven"
};
private
int
frameheight;
// 图片的高度
private
int
window_width;
// 屏幕宽度
private
ArrayList<ImageView> imageViews;
// ponit 集合
private
ArrayList<View> views;
// flipper的孩子
private
Timer timer;
/***
* 初始化 point
*/
void
initPoint() {
imageViews =
new
ArrayList<ImageView>();
ImageView imageView;
for
(
int
i =
0
; i < image_id.length; i++) {
imageView =
new
ImageView(
this
);
imageView.setBackgroundResource(R.drawable.indicator);
LinearLayout.LayoutParams layoutParams =
new
LinearLayout.LayoutParams(
new
ViewGroup.LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
layoutParams.leftMargin =
10
;
layoutParams.rightMargin =
10
;
ll_point.addView(imageView, layoutParams);
imageViews.add(imageView);
}
}
/***
* ChildView
*/
void
initChildView(ViewFlipper flipper) {
views =
new
ArrayList<View>();
LayoutParams layoutParams =
new
LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.FILL_PARENT);
for
(
int
i =
0
; i < image_id.length; i++) {
ImageView imageView =
new
ImageView(
this
);
imageView.setScaleType(ScaleType.FIT_XY);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),
image_id[i]);
Bitmap bitmap2 = getBitmap(bitmap, window_width);
frameheight = bitmap2.getHeight();
// 获取要显示的高度
imageView.setImageResource(image_id[i]);
flipper.addView(imageView, layoutParams);
views.add(imageView);
}
initPoint();
}
/***
* 初始化 HeadImage
*/
void
initHeadImage() {
layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View headview = layoutInflater.inflate(R.layout.head_image,
null
);
flipper = (ViewFlipper) headview.findViewById(R.id.ViewFlipper01);
ll_point = (LinearLayout) headview.findViewById(R.id.ll_point);
frameLayout = (FrameLayout) headview.findViewById(R.id.fl_main);
initChildView(flipper);
LayoutParams layoutParams = (LayoutParams) frameLayout
.getLayoutParams();
layoutParams.height = frameheight;
frameLayout.setLayoutParams(layoutParams);
draw_Point(
0
);
// 默认首次进入
lv_main.addHeaderView(headview);
// 要卸载setAdapter前面
lv_main.setAdapter(
new
ArrayAdapter<String>(
this
,
android.R.layout.simple_list_item_1, msg));
}
/***
* init view
*/
void
initView() {
setTitle(
"jjhappyforever..."
);
setContentView(R.layout.main);
lv_main = (ListView) findViewById(R.id.lv_main);
lv_main.setOnItemClickListener(
new
OnItemClickListener() {
@Override
public
void
onItemClick(AdapterView<?> parent, View view,
int
position,
long
id) {
if
(position !=
0
)
Toast.makeText(MainActivity.
this
, msg[position -
1
],
1
)
.show();
else
{
int
index = getPageIndex(flipper.getCurrentView());
Toast.makeText(MainActivity.
this
,
"图"
+ index,
1
).show();
}
}
});
initHeadImage();
}
/***
* 更新选中点
*
* @param index
*/
private
void
draw_Point(
int
index) {
for
(
int
i =
0
; i < imageViews.size(); i++) {
imageViews.get(i).setImageResource(R.drawable.indicator);
}
imageViews.get(index).setImageResource(R.drawable.indicator_focused);
}
@Override
public
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 获取屏幕的宽度
window_width = (
int
) getResources().getDimension(R.dimen.window_width);
detector =
new
GestureDetector(
this
);
initView();
timer =
new
Timer(
true
);
timer.schedule(
new
TimerTask() {
@Override
public
void
run() {
runOnUiThread(
new
Runnable() {
@Override
public
void
run() {
int
pageIndex = getPageIndex(flipper.getCurrentView());
if
(pageIndex == flipper.getChildCount() -
1
)
pageIndex =
0
;
else
pageIndex++;
flipper.setInAnimation(AnimationUtils.loadAnimation(
MainActivity.
this
, R.anim.push_right_in));
flipper.setOutAnimation(AnimationUtils.loadAnimation(
MainActivity.
this
, R.anim.push_left_out));
flipper.showNext();
draw_Point(pageIndex);
}
});
}
},
5000
,
5000
);
}
/***
* 对图片处理
*
* @author zhangjia
*
*/
Bitmap getBitmap(Bitmap bitmap,
int
width) {
int
w = bitmap.getWidth();
int
h = bitmap.getHeight();
Matrix matrix =
new
Matrix();
float
scale = (
float
) width / w;
// 保证图片不变形.
matrix.postScale(scale, scale);
// w,h是原图的属性.
return
Bitmap.createBitmap(bitmap,
0
,
0
, w, h, matrix,
true
);
}
@Override
public
boolean
dispatchTouchEvent(MotionEvent ev) {
this
.detector.onTouchEvent(ev);
return
super
.dispatchTouchEvent(ev);
}
@Override
public
boolean
onDown(MotionEvent e) {
return
true
;
}
/***
* 返回当前第几屏
*/
int
getPageIndex(View view) {
for
(
int
i =
0
; i < views.size(); i++) {
if
(view == views.get(i))
return
i;
}
return
0
;
}
/**
* 监听滑动
*/
@Override
public
boolean
onFling(MotionEvent e1, MotionEvent e2,
float
velocityX,
float
velocityY) {
int
pageIndex = getPageIndex(flipper.getCurrentView());
// 左划
if
(e1.getX() - e2.getX() >
120
) {
this
.flipper.setInAnimation(AnimationUtils.loadAnimation(
this
,
R.anim.push_right_in));
this
.flipper.setOutAnimation(AnimationUtils.loadAnimation(
this
,
R.anim.push_left_out));
this
.flipper.showNext();
if
(pageIndex == flipper.getChildCount() -
1
)
draw_Point(
0
);
else
draw_Point(++pageIndex);
return
true
;
// 右划
}
else
if
(e1.getX() - e2.getX() < -
120
) {
this
.flipper.setInAnimation(AnimationUtils.loadAnimation(
this
,
R.anim.push_left_in));
this
.flipper.setOutAnimation(AnimationUtils.loadAnimation(
this
,
R.anim.push_right_out));
this
.flipper.showPrevious();
if
(pageIndex ==
0
)
draw_Point(flipper.getChildCount() -
1
);
else
draw_Point(--pageIndex);
return
true
;
}
return
true
;
}
@Override
public
void
onLongPress(MotionEvent e) {
}
@Override
public
boolean
onScroll(MotionEvent e1, MotionEvent e2,
float
distanceX,
float
distanceY) {
return
false
;
}
@Override
public
void
onShowPress(MotionEvent e) {
}
@Override
public
boolean
onSingleTapUp(MotionEvent e) {
return
false
;
}
}</span>
|
上诉代码写的有点小乱,别介意.
效果:
你可以手势左右滑动图片切换,由于我们加入了动画,则在切换图片效果会比较人性,这一点比较不错.另外一点,我开启了timer,让它自己切换,感觉这点比较不错,可惜好多应用都没有这么搞,总之实现就行了,我们开发人员嘛,就是开发别人想出来的东西,感慨程序员苦逼...
如果你按照上诉操作的话会有几个问题:1,我移动图片下面的item图片也会切换,2,我在滑动切换图片的时候偶尔也会执行onclick事件,这两点bug严重不允许,为之我也煞费神经细胞啊,没办法因为基础不好,对触摸种种事件还是搞不明白,有时间了还得在看看研究研究,扯远了,下面我说下解决方法:
第一:我只让listview的第一项监听手势操作,其他的不执行.
方法很简单,自定义一个listview.重写其onTouchEvent事件.
01
02
03
04
05
06
07
08
09
10
11
12
13
|
@Override
public
boolean
onTouchEvent(MotionEvent ev) {
Log.e(
"jj"
,
"onTouchEvent..."
);
int
x = (
int
) ev.getX();
int
y = (
int
) ev.getY();
int
position = pointToPosition(x, y);
// 只有headview才进行手势操作.
if
(position ==
0
) {
// 注入手势
gestureDetector.onTouchEvent(ev);
}
return
super
.onTouchEvent(ev);
}
|
大家一看就明白了,我们只对position==0进行手势监听,也许有人问了,其实也可以直接在MainActivity中的dispatchTouchEvent分发事件中获取点击listview的position,可是这样不准确,我点击第0项获取的有的是0,有的是1,原因目前不明,不过但可以肯定,这样是能获取listview的position的,所以就干脆自定义吧,这样不会出错.这样解决了不会滑动下面item图片跟着切换.
再有就是我们要把listview item的第一项 onclick事件禁止了,我们直接把这个点击事件搬到onSingleTapUp中,这样就不会因为手势操作而影响item的onclick事件了,这样问题基本都解决了,其实我想有简单的方法,只要把Touch事件弄懂,可惜啊...不给力啊...
效果和上面一样.
经过多次测试,目前没有发现问题,如有不妥我会给出提示.
第二种方法:ViewPager.
viewpager效果相比大家都熟知,因此我就省略显示的那部分,方法和上面一样,只是显示用的是viewpager而已.
但是这里面存在一个严重的问题:ViewPager和listview共存的问题,二者都有自身的滑动事件,必然要产生冲突。viewpager操作起来相当的不灵敏.
这里我重点说一下解决办法:我们需要自定义Listview,对其拦截事件进行处理.另外我们要用手势,判断上下左右滑动.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
package
com.jj.chage;
import
android.content.Context;
import
android.util.AttributeSet;
import
android.util.Log;
import
android.view.GestureDetector;
import
android.view.GestureDetector.SimpleOnGestureListener;
import
android.view.MotionEvent;
import
android.view.View;
import
android.widget.ListView;
public
class
MyListView
extends
ListView {
private
GestureDetector mGestureDetector;
View.OnTouchListener mGestureListener;
public
MyListView(Context context) {
super
(context);
}
public
MyListView(Context context, AttributeSet attrs) {
super
(context, attrs);
mGestureDetector =
new
GestureDetector(
new
YScrollDetector());
setFadingEdgeLength(
0
);
}
public
MyListView(Context context, AttributeSet attrs,
int
defStyle) {
super
(context, attrs, defStyle);
}
@Override
public
boolean
onInterceptTouchEvent(MotionEvent ev) {
super
.onInterceptTouchEvent(ev);
return
mGestureDetector.onTouchEvent(ev);
}
class
YScrollDetector
extends
SimpleOnGestureListener {
@Override
public
boolean
onScroll(MotionEvent e1, MotionEvent e2,
float
distanceX,
float
distanceY) {
if
(Math.abs(distanceY) >= Math.abs(distanceX)) {
Log.e(
"jj"
,
"上下...."
);
return
true
;
}
Log.e(
"jj"
,
"左右...."
);
return
false
;
}
}
}
|
这样viewpager滑动就不会受listview干扰了,listview上下也可以滑动.
由于自己对事件分发不是很了解,所以不过多介绍,想知道的话,自己慢慢研究吧,我这里只是提供一个解决方法,我也在学习中...其他部分不难,这里就不讲解了.
滑动ing 滑动ing 点击
感觉还是第二种方法好,这也是为什么那么多客户端都是这么搞,不过各有千秋,因人而异.
对第二种方法实现简单讲解:
我们的目的是:我们左右滑动ViewPager的时候ListView不影响,而当ViewPager上下滑动的时候可以随意滑动.
我们可以这样做:我们把onInterceptTouchEvent返回值更改为fase,那么意味着,如果孩子存在onInterceptTouchEvent那么会继续传递给孩子的onInterceptTouchEvent...后面我们不管(此时ListView失去touch事件),这个时候ViewPager获取Touch事件. 这个时候ViewPager就可以左右滑动(不可以上下滑动)。 如果孩子不存在onInterceptTouchEvent,ListView执行本身ontouch.
那么把onInterceptTouchEvent返回值更改为true.意思就是:我对touch事件进行拦截,不进行向下传递,直接执行自身的Ontouch事件,这个时候ViewPager就可以上下滑了(不可以左右滑动切换).
根据实情,那么我们如何动态控制这个onInterceptTouchEvent的返回值,这个时候我们可以借助:GestureDetector手势来实现.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
/***
*
* @author zhangjia
*
*/
class
YScrollDetector
extends
SimpleOnGestureListener {
@Override
public
boolean
onScroll(MotionEvent e1, MotionEvent e2,
float
distanceX,
float
distanceY) {
if
(Math.abs(distanceY) >= Math.abs(distanceX)) {
Log.e(
"jj"
,
"上下...."
);
return
true
;
}
Log.e(
"jj"
,
"左右...."
);
return
false
;
}
|
上面这个方法可以根据手势来判断我们手的滑动方向.而:boolean b = mGestureDetector.onTouchEvent(ev);
这个值就是onScroll返回的值.这个值是代表我们手势mGestureDetector消费了没,为什么这么说呢,因为这个和我们外界的touch分开了,就算我们在这里消费了那么外面该怎么执行就怎么执行。经过测试觉得mGestureDetector.onTouchEvent(ev)这个方法就是执行手势相应方法,然后返回的是onScroll的返回值.
而当我们上下滑动的时候mGestureDetector.onTouchEvent(ev)返回true,而ViewPager需要上下滑动的时候只需要将onInterceptTouchEvent的返回值更改为true,左右滑动同理.
那么这样我们就实现了ViewPager与ListView滑动的冲突.