一个超炫的listview

系统自带的一些东西总是让我们不太满意, 对于Android开发来说,自定义一些东西也不是很难,但是仅局限于Android系统提供的那些自定义的一些方式,自己感觉Google官方给开发者自定义一些东西有一些局限性,对于一个普通开发者来说,如果不去深入Android源码的话,要做一个比较好的效果,特别是那种很不常规,不常见的一些东西,就会显得碍手碍脚,各种坑就会随之而来. 但程序员注定就是要生命不息,折腾不止.所以没事折腾折腾,做点炫酷的东西~~


好了,先废话不多说,上图看效果,觉得效果很一般的,那就不要浪费时间了, 觉得效果不错的可以继续研究下去:
这里写图片描述
先说说自己的思路吧:

首先,简单分析可知: 其实就是根据手指的滑动,动态改变每一个item的高度. 监听listview的滑动,可以重写Listview的onScroll方法,在这里我们可以知道listview滑动到哪儿了, 然后根据滑动的位置,动态修改每一个item的高度, 到这里有一个矛盾,你修改了item的高度,就会间接的让listview滚动,然后又修改.很有可能陷入一个死循环中. 而且每一个item的高度是否可动态调整也是不确定的,就算高度可调整了,每个item的高度的计算也不是件容易的事. 现在想想可真是困难重重啊!!!
知难而上,越挫越勇! 作为一个程序员应该有的素质,我必须去尝试一下,不成功也会有收获!

一步一步来:首先把Listview的滚动事件拿到吧:
写一个MagnifyListView继承自ListView,重写onScroll方法

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        int myScrollY = getMyScrollY();
    //到这里我们就可以获取到listview的滚动事件了
    }

    /**
     * 获取当前listView的Y轴的滚动距离
     * @return
     */
    public int getMyScrollY() {
         View c = this.getChildAt(0);
         if (c == null ) {
             return 0;
         }
         int firstVisiblePosition = getFirstVisiblePosition();
         int top = c.getTop();
         return -top + firstVisiblePosition * c.getHeight() ;
    }

现在的任务就是把滚动的数据让每一个item的View知道,它们知道listview滚动到哪儿才能调整自己的高度嘛:
所以,我要自定一个MyChangeLinearLayout继承自LinearLayout,以后item布局中都要用这个view来作为最外层的view

主要功能如下:

public class MyChangeLinearLayout extends LinearLayout{
    //其他该有的方法我就不写了,会在最后面附上完整的源码
    /**
     * 在这里写一个方法用了让刚才那个自定义Listview告诉当前这个item的一些滚动数据
     * @param scrollY 当前listview的Y轴滚动距离
     * @param currentItem  当前这个itemView在listview的子view中的位置,注意不是真实数据中的位置,因为listview的item是重复利用的
     */
    public void onItemChange(int scrollY,int currentItem){

    }
}

到这里就要让listview把滚动信息告诉每一个itemView了,上代码:

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ListView;

public class MagnifyListView extends ListView implements OnScrollListener{

    private int minHeight;
    private int maxHeight;

    public MagnifyListView(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
        setOnScrollListener(this);
    }

    public MagnifyListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
        setOnScrollListener(this);
    }

    public MagnifyListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        setOnScrollListener(this);
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {

    }

    private int lastScroll;

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        int myScrollY = getMyScrollY();
        //防止进入死循环
        if (lastScroll!=myScrollY) {
            for (int i = 0; i < visibleItemCount; i++) {
                MyChangeLinearLayout childAt = (MyChangeLinearLayout) getChildAt(i);
                childAt.onItemChange(myScrollY, i);
            }
        }
        lastScroll=myScrollY;

    }

    /**
     * 获取当前listView的Y轴的滚动距离
     * @return
     */
    public int getMyScrollY() {
         View c = this.getChildAt(0);
         if (c == null ) {
             return 0;
         }
         int firstVisiblePosition = getFirstVisiblePosition();
         int top = c.getTop();
         return -top + firstVisiblePosition * c.getHeight() ;
    }
}

好了,到这里,每一个item的view都知道滚动到了哪儿,自己身处的位置,现在就要根据这些信息来调整自己的高度了,
调整方式如下:
这里写图片描述
上代码:

import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;

public class MyChangeLinearLayout extends LinearLayout{

    public static int minheight = 250;//view的最小高度
    public static int maxheight = 500;//view的最大高度

    public static float startAlpha = 0.2f;//view收缩状态时的透明度
    public static float endAlpha = 1;//view展开状态时的透明度

    public static int endBigLoc = maxheight/2+100;//完全展开时的位置
    public static int startBigLoc = endBigLoc+minheight*3+120;//完全收缩时的位置

    public MyChangeLinearLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        // TODO Auto-generated constructor stub
    }

    public MyChangeLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
    }

    public MyChangeLinearLayout(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
    }

    private boolean isLast = false;//最后几个设置为透明

    public void setLast(boolean b){
        isLast=b;
    }

    /**
     * 在这里写一个方法用了让刚才那个自定义Listview告诉当前这个item的一些滚动数据
     * @param scrollY 当前listview的Y轴滚动距离
     * @param currentItem  当前这个itemView在listview的子view中的位置,注意不是真实数据中的位置,因为listview的item是重复利用的
     */
    public void onItemChange(int scrollY,int currentItem){

//      L.e("第"+currentItem+":"+scrollY);

        if (scrollY%maxheight==0) {
            return;
        }

        //计算距离顶部的距离
        int itemTop = getItemTop(currentItem, scrollY);
        int itemHerght = getCurrentItemHerght(itemTop);
        //最后一个是全透明的,为了保证最后一条数据能被完全展开
        if (isLast) {
            setAlpha(0);
            ViewGroup parent = (ViewGroup) getParent();
            setMinimumHeight(parent.getHeight()-endBigLoc-maxheight);
        }else{
            setAlpha(getCurrentItemAlpha(itemTop, startAlpha, endAlpha));
            setMinimumHeight(itemHerght);
        }
        View childAt = getChildAt(0);
        LinearLayout.LayoutParams layoutParams = (LayoutParams) childAt.getLayoutParams();
        layoutParams.height=itemHerght;

//      if (currentItem==0||currentItem==1) {
//          L.e("di:"+currentItem+" :"+itemHerght);
//      }
    }

    /**
     * 计算当前item的距离顶部的距离
     * @param item
     * @return
     */
    private int getItemTop(int item,int scrollY){
        if (item==0) {
            return -(scrollY%maxheight);
        }
        int itemTop = getItemTop(item-1,scrollY);
        int currentItemHerght = getCurrentItemHerght(itemTop);
//      L.e("第"+item+"  "+(currentItemHerght+itemTop));
        return currentItemHerght+itemTop;

    }

    /**
     * 根据当前item距离顶部的距离,计算当前item的高度
     * @param top
     * @return
     */
    private int getCurrentItemHerght(int top){
        if (top<=endBigLoc) {
//          setMinimumHeight(height+dheight);
            return maxheight;
        }else if(top>=startBigLoc){
//          setMinimumHeight(height);
            return minheight;
        }else{
            //渐变
            int h=(int) (minheight+((startBigLoc-top+0.0)/(startBigLoc-endBigLoc))*(maxheight-minheight));
//          L.e("调整:"+currentItem+"  top:"+top+"  gaodu:"+h);
//          setMinimumHeight(h);
            return h;
        }
    }
    /**
     * 根据当前item距离顶部的距离,计算当前item的透明度
     * @param top
     * @return
     */
    private float getCurrentItemAlpha(int top,float start,float end){
        if (top<=endBigLoc) {
            return end;
        }else if(top>=startBigLoc){
            return start;
        }else{
            //渐变
            float h=start+((startBigLoc-top+0.0f)/(startBigLoc-endBigLoc))*(end-start);

            return h;
        }
    }

}

到这里看似任务都完成了,其实还有一个问题, 就是listview的最后几个item似乎永远都无法展开,因为listview滑到底了不可能再往上滑, 我的解决思路是让最后一个item变成透明的并且高度要足够高,能让倒数第二个的item完全展开. 这些代码又改写在哪儿呢? 好像只有adapter知道数据是否到了最后一个,所以只能自己再自定义一个adapter:
核心代码:

    @Override
    public int getCount() {
        //让adapter中的item数量比真实数据多一个
        return getMyCount()+1;
    }
    @Override
    public View getView(int item, View view, ViewGroup arg2) {
        //...balabala省略
        MyChangeLinearLayout v = (MyChangeLinearLayout) view;
        //告诉MyChangeLinearLayout 是否是是数据中的最后一个,来让MyChangeLinearLayout 变透明变得足够的高
        if (getCount()-1==item) {
            v.setLast(true);
        }else{
            v.setLast(false);
        }


        return view;
    }

adapter的代码我简单封装了一下,继承自这个adapter就可以了:

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;

import com.yuc.yuc_magnifyitemlistview.R;
import com.yuc.yuc_magnifyitemlistview.view.MyChangeLinearLayout;

public abstract class MagnifyListviewAdapter extends BaseAdapter{

    protected Context context;
//  private LayoutInflater inflater;

    public  MagnifyListviewAdapter(Context context) {
        this.context=context;

        MyChangeLinearLayout.minheight=getMinheight();
        MyChangeLinearLayout.maxheight=getMaxheight();
        MyChangeLinearLayout.endBigLoc=getEndBigLoc();
        MyChangeLinearLayout.startBigLoc=getStartBigLoc();

    }

    @Override
    public int getCount() {
        return getMyCount()+1;
    }

    public abstract int getMyCount();//这里用getMyCount代替getCount

    @Override
    public View getView(int item, View view, ViewGroup arg2) {

        if (view==null) {
            LayoutInflater inflater = LayoutInflater.from(context);
            view = inflater.inflate(R.layout.listview_magnify_item_layout, null);
            LinearLayout listview_item_ll = (LinearLayout) view.findViewById(R.id.listview_item_ll);
            listview_item_ll.addView(getMyView(item, null, arg2));

            //初始化高度
            MyChangeLinearLayout v = (MyChangeLinearLayout) view;
            v.onItemChange(1, item);
        }else{
            view = getMyView(item, view, arg2);

            view.setMinimumHeight(getMaxheight());
            View childAt = ((ViewGroup)view).getChildAt(0);
            LinearLayout.LayoutParams layoutParams = (LayoutParams) childAt.getLayoutParams();
            layoutParams.height=getMaxheight();
        }


        MyChangeLinearLayout v = (MyChangeLinearLayout) view;
        if (getCount()-1==item) {
            v.setLast(true);
        }else{
            v.setLast(false);
        }


        return view;
    }

    public abstract View getMyView(int item, View view, ViewGroup arg2);

    /**
     * 最小高度
     * @return
     */
    public abstract int getMinheight();
    /**
     * 最大高度
     * @return
     */
    public abstract int getMaxheight();
    /**
     * 完全展开位置-Y轴
     * @return
     */
    public abstract int getEndBigLoc();
    /**
     * 完全收缩位置-Y轴
     * @return
     */
    public abstract int getStartBigLoc();

}

好了,控件都已准备完毕,现在让我们试试:
生成MainActivity:
布局文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <com.yuc.yuc_magnifyitemlistview.view.MagnifyListView
        android:id="@+id/lv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#000000"
        android:divider="@null"
        ></com.yuc.yuc_magnifyitemlistview.view.MagnifyListView>

</RelativeLayout>

Java代码:

import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;

import com.yuc.yuc_magnifyitemlistview.adapter.DemoListAdapter;

public class MainActivity extends Activity {

    private ListView lv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        lv = (ListView) findViewById(R.id.lv);
        DemoListAdapter adapter = new DemoListAdapter(this);
        lv.setAdapter(adapter);

    }
}

还有adapter,继承自MagnifyListviewAdapter就可以了:

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.yuc.yuc_magnifyitemlistview.R;
import com.yuc.yuc_magnifyitemlistview.util.DensityUtils;

public class DemoListAdapter extends MagnifyListviewAdapter{

    private LayoutInflater inflater;

    public DemoListAdapter(Context context){
        super(context);
        inflater = LayoutInflater.from(context);
    }

    @Override
    public Object getItem(int arg0) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public long getItemId(int arg0) {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public int getMyCount() {
        return 30;
    }

    @Override
    public View getMyView(int item, View view, ViewGroup arg2) {

        if (view==null) {
            view = inflater.inflate(R.layout.commodity_list_item_layout, null);
        }

        return view;

    }

    @Override
    public int getMinheight() {
        return DensityUtils.dp2px(context, 50);
    }

    @Override
    public int getMaxheight() {
        return DensityUtils.dp2px(context, 150);
    }

    @Override
    public int getEndBigLoc() {
        return getMaxheight()/2;
    }

    @Override
    public int getStartBigLoc() {
        return (int) (getEndBigLoc()+getMaxheight()*2.5);
    }


}

item的布局文件:

<?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="wrap_content"
    android:orientation="vertical"
    >
    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"
        android:src="@drawable/community1" />
</LinearLayout>

搞定! 使用起来是不是很简单,完全没有改变原始的使用习惯~~!
好了到这里就该结束了,送上源码:
https://github.com/yuchong123/MagnifyItemListView

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值