首先我们还是看一些示例:(网易,新浪,百度)
显示效果都不错,可是手感就不一样了,百度最棒,网易还行,新浪就操作很不好,这里我说的是滑动切换图片.自己可以测试一下.不得不说牛叉的公司确实有哦牛叉的道理.
下面我简单的介绍下实现方法:其实就是listviewaddHeaderView.只不过这个view是一个可以切换图片的view,至于这个view怎么做,就要根据自己的喜爱了,实现有多种方法,下面我简单介绍一下.
第一种:ViewFlipper+GestureDetector
主布局就是一个listview,这里就不介绍了,我介绍下切换图片布局head_iamge.xml
这里我就添加一系列切换点,至于显示新闻标题,透明效果等等,大家可以自己布局,方法同理,不难实现.
接下来我们看动画布局.
push_left_in.xml
push_left_out.xml
push_right_in.xml
push_right_out.xml
我简单介绍下这些布局:
push_left_in:左边进入,则要进入的view初始位置在-100%p位置,终止位置在0,而push_left_out:左边出来,则此时view的位置在0,而终止位置在-100%p.
右进右出同理,至于alpha渐变,很简单,动画就说道这里,相信了解动画的同学们不用看就ok了.
下面重点是如何实现.
代码:
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 imageViews;// ponit 集合
private ArrayList views;// flipper的孩子
private Timer timer;
/***
* 初始化 point
*/
void initPoint() {
imageViews = new ArrayList();
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();
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(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;
}
}
上诉代码写的有点小乱,别介意.
效果:
你可以手势左右滑动图片切换,由于我们加入了动画,则在切换图片效果会比较人性,这一点比较不错.另外一点,我开启了timer,让它自己切换,感觉这点比较不错,可惜好多应用都没有这么搞,总之实现就行了,我们开发人员嘛,就是开发别人想出来的东西,感慨程序员苦逼...
如果你按照上诉操作的话会有几个问题:1,我移动图片下面的item图片也会切换,2,我在滑动切换图片的时候偶尔也会执行onclick事件,这两点bug严重不允许,为之我也煞费神经细胞啊,没办法因为基础不好,对触摸种种事件还是搞不明白,有时间了还得在看看研究研究,扯远了,下面我说下解决方法:
第一:我只让listview的第一项监听手势操作,其他的不执行.
方法很简单,自定义一个listview.重写其onTouchEvent事件.
@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);
}
再有就是我们要把listview item的第一项 onclick事件禁止了,我们直接把这个点击事件搬到onSingleTapUp中,这样就不会因为手势操作而影响item的onclick事件了,这样问题基本都解决了,其实我想有简单的方法,只要把Touch事件弄懂,可惜啊...不给力啊...
效果和上面一样.
经过多次测试,目前没有发现问题,如有不妥我会给出提示.
第二种方法:ViewPager.
viewpager效果相比大家都熟知,因此我就省略显示的那部分,方法和上面一样,只是显示用的是viewpager而已.
但是这里面存在一个严重的问题:ViewPager和listview共存的问题,二者都有自身的滑动事件,必然要产生冲突。viewpager操作起来相当的不灵敏.
这里我重点说一下解决办法:我们需要自定义Listview,对其拦截事件进行处理.另外我们要用手势,判断上下左右滑动.
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手势来实现.
/***
*
* @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滑动的冲突.
就说道这里,如有错误和疑问请留言.
我将源码上传网上,如有不懂自行下载,注释已很明确.
如有问题请留言,对你有帮助的话,记得赞一个哦.
在此 thanks for you .
源码一
源码二