android ListView加HeadView左右切换图片(类似各大新闻客户端)

         我简单的介绍下实现方法:其实就是listview addHeaderView.只不过这个view是一个可以切换图片的view,至于这个view怎么做,就要根据自己的喜爱了,实现有多种方法,下面我简单介绍一下.

第一种:ViewFlipper+GestureDetector

主布局就是一个listview,这里就不介绍了,我介绍下切换图片布局

head_iamge.xml

<?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>

这里我就添加一系列切换点,至于显示新闻标题,透明效果等等,大家可以自己布局,方法同理,不难实现.

接下来我们看动画布局.

push_left_in.xml

<?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>

push_left_out.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >

    <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: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>

push_right_out.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >

    <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,大家可以拿去看看!

http://download.csdn.net/detail/gulaer/4965682

时间: 2024-11-03 13:04:10

android ListView加HeadView左右切换图片(类似各大新闻客户端)的相关文章

android listview的headview

问题描述 android listview的headview 用别人的headview,为什么我的headview只显示了一半..是不是哪里设置问题 解决方案 解决了,listview的布局有个地方用了wrap_content,换成match_parent就好了 解决方案二: 明显是布局出了问题,改一下图片的伸缩方式就可以了 解决方案三: 你的布局拿出来看看,可能是高度设置有问题 解决方案四: <?xml version="1.0" encoding="utf-8&qu

Android Listview 滑动过程中提示图片重复错乱的原因及解决方法_Android

主要分析Android中Listview滚动过程造成的图片显示重复.错乱.闪烁的原因及解决方法,顺便跟进Listview的缓存机制. 1.原因分析 Listview item 缓存机制:为了使得性能更优,Listview会缓存行item(某行对应的view).listview通过adapter的getview函数获得每行的item.滑动过程中, a.如果某行item已经划出屏幕,若该item不在缓存内,则put进缓存,否则更新缓存: b.获取滑入屏幕的行item之前会先判断缓存中是否有可用的it

android关于加载 2049*1376 图片问题

问题描述 android关于加载 2049*1376 图片问题 <?xml version="1.0" encoding="utf-8"?> android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" android:background="@

android imageview加载网络图片无图片

问题描述 android imageview加载网络图片无图片 MainActivity.java package study_imageput.com.study_apktointent; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.widget.ImageV

wpf 将文件群分组,用listview加载文件和图片,但是还是有内存消耗问题

问题描述 我用listview模拟文件浏览界面,最后由于加载量过大而内存消耗量的问题,最后改成"将文件夹中便利的文件路径进行分组,一组是一个集合,每组有二十个文件路径,每次加载二十个文件,"的方式,但是每次加载内存还是会增高不减,是我程序设计有问题么?源代码:处理加载一组文件的类classData:System.IDisposable{publicstaticintfirstDate;//传输第一个文件路径组以及之后加载上一组或下一组文件路径组publicstaticList<L

Android异步加载数据和图片的保存思路详解_Android

把从网络获取的图片数据保存在SD卡上, 先把权限都加上 网络权限 android.permission.INTERNET SD卡读写权限 android.permission.MOUNT_UNMOUNT_FILESYSTEMS android.permission.WRITE_EXTERNAL_STORAGE 总体布局 写界面,使用ListView,创建条目的布局文件,水平摆放的ImageView TextView 在activity中获取到ListView对象,调用setAdapter()方法

新浪微博-Android加载网络GIF图片

问题描述 Android加载网络GIF图片 我用URLConnection加载网络的GIF图片,然后播放,图片大小在3M-5M左右,第一次加载的时候,要花费30-40秒左右,但我观察新浪微博的GIF加载比较快, 基本10秒左右就完成的,保存原图后,图片也在4M左右,想问一下,是怎么实现的?都是第一次加载,缓存中还没有 解决方案 Android 加载.gif格式图片Android ListView加载网络数据和图片android listview加载网络图片 解决方案二: 建议你用Glide,Go

解决Android ListView数据为空及加载错误的方法_Android

在项目中,都会用到ListView或GridView等列表控件.一般会用来展示从网络请求的数据 .如果请求的数据为空或者在请求的时候正好无没有网络了,我们的界面应该如何展示呢?数据为空的时候,ListView可以使用setEmptyView (View emptyView) 方法来我们需要的统一界面.数据加载失败呢?我们也可以统一进行处理. //下面这个类是简单地封装用于无数据及加载错误的一个页面. public class CommonShowView { private Context mC

Android ListView滚动到底后自动加载数据_Android

熟悉Android的朋友们都知道,不管是微博客户端还是新闻客户端,都离不开列表组件,可以说列表组件是Android数据展现方面最重要的组件,我们 今天就要讲一讲列表组件ListView加载数据的相关内容.通常来说,一个应用在展现大量数据时,不会将全部的可用数据都呈现给用户,因为这不管对于服 务端还是客户端来说都是不小的压力,因此,很多应用都是采用分批次加载的形式来获取用户所需的数据.比如:微博客户端可能会在用户滑动至列表底端时自动加 载下一页数据,也可能在底部放置一个"加载更多"按钮,