android ListView加HeadView左右切換圖片(類似各大新聞客戶端)
我簡單的介紹下實現方法:其實就是listview addHeaderView.隻不過這個view是一個可以切換圖片的view,至於這個view怎麼做,就要根據自己的喜愛了,實現有多種方法,下麵我簡單介紹一下.
第一種:ViewFlipper+GestureDetector
主布局就是一個listview,這裏就不介紹了,我介紹下切換圖片布局
head_iamge.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:andro
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<FrameLayout
android:
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<ViewFlipper
android:
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
</ViewFlipper>
<LinearLayout
android:
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>
這裏我就添加一係列切換點,至於顯示新聞標題,透明效果等等,大家可以自己布局,方法同理,不難實現.
接下來我們看動畫布局.
push_left_in.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:andro
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>
push_left_out.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:andro >
<translate
android:duration="500"
android:fromXDelta="0"
android:toXDelta="-100%p" />
<alpha
android:duration="500"
android:fromAlpha="1.0"
android:toAlpha="0.5" />
</set>
push_right_in.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:andro
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>
push_right_out.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:andro >
<translate
android:duration="500"
android:fromXDelta="0"
android:toXDelta="100%p" />
<alpha
android:duration="500"
android:fromAlpha="1.0"
android:toAlpha="0.5" />
</set>
我簡單介紹下這些布局:
push_left_in:左邊進入,則要進入的view初始位置在-100%p位置,終止位置在0,而push_left_out:左邊出來,則此時view的位置在0,而終止位置在-100%p.
右進右出同理,至於alpha漸變,很簡單,動畫就說道這裏,相信了解動畫的同學們不用看就ok了.
下麵重點是如何實現.
代碼:
MainActivity.java
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;
}
}


你可以手勢左右滑動圖片切換,由於我們加入了動畫,則在切換圖片效果會比較人性,這一點比較不錯.另外一點,我開啟了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);
}
大家一看就明白了,我們隻對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,對其攔截事件進行處理.另外我們要用手勢,判斷上下左右滑動.
MyListView.java
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上下也可以滑動.
由於自己對事件分發不是很了解,所以不過多介紹,想知道的話,自己慢慢研究吧,我這裏隻是提供一個解決方法,我也在學習中...其他部分不難,這裏就不講解了.
感覺還是第二種方法好,這也是為什麼那麼多客戶端都是這麼搞,不過各有千秋,因人而異.
對第二種方法實現簡單講解:
我們的目的是:我們左右滑動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滑動的衝突.
好了,就寫到這裏了,下麵給一個Demo,大家可以拿去看看!
https://download.csdn.net/detail/gulaer/4965682
最後更新:2017-04-04 07:03:27