0
回答
ListView实现类似Gallery的功能
利用AWS快速构建适用于生产的无服务器应用程序,免费试用12个月>>>   

原文转自:http://marshal.easymorse.com/archives/3851

利用ListView,实现类似Android Gallery的功能。效果类似这样:

imageimage

长按列表中元素:

image

勾选列表中元素,点击“选择”按钮:

imageimage

点击删除按钮,则删掉所选元素:

imageimage

再次长按屏幕中元素,按钮和多选框消失。另外,按系统回退按钮也可使按钮和多选框消失。如果没有多选框和按钮的时候,回退按钮将执行系统操作,退出应用。

实现这部分代码有几个难点:

  • 长按界面中元素,所有元素都显示CheckBox,当然可以遍历所有元素(视图)并设置相应的CheckBox为visible,这样逻辑会比较乱,本例中使用了java提供的对观察者模式的支持类
  • 删除其中某几个元素,应该保持在当前位置,其他相邻元素填补删除的空缺

下面说说难点的实现方法。先说观察者模式。实际上是引入了第三个对象,可观察对象,你可以把它看作中间人,所有元素都在这个中间人对象上注册了监听器,当其中一个被长按后,通知所有元素设置checkbox。

可以通过:

http://www.java2s.com/Code/Java/Design-Pattern/AsimpledemoofObservableandObserver.htm

中的示例了解观察者模式以及java Observer的使用方法。

在本例中,通过内部类实现了Observable接口:

class MyObservable extends Observable { 
    public void toobarStatusChanged(boolean visible) { 
        setChanged(); 
        notifyObservers(visible); 
    } 
}

这里实现了Observer的继承类,并为每个元素创建示例注册到Observable中:

// 创建观察者,用于监控是否有Checkbox可见性事件,然后加入到可观察对象中 
Observer observer = new Observer() { 
    @Override 
    public void update(Observable observable, Object data) { 
        checkBox.setVisibility((Boolean) data ? View.VISIBLE 
                : View.INVISIBLE); 
        checkBox.setChecked(checkedIds.contains(index)); 
    } 
}; 
observable.addObserver(observer);

另外,实现了一个Observer实例用于监控长按事件的变化,来生成或者取消工具条按钮。

这个实现目前可以工作,但是可能还有问题,造成大量的Observer对象的存在,因为每次创建ListView的Row都会重新创建,等有时间再解决。

如何做到删除其中几个元素,后面相邻元素自动补位,而且屏幕保持在当前位置。我的做法基本思路是,取到当前屏幕显示的各行,在Nexusone的分 辨率下可取到3行,然后获取各行的ViewGroup对象,清空它的子视图,然后再根据数据源(本文是一个List)重新填充行的内容。

如何得到当前在屏幕的行的ViewGroup呢?其实并不复杂:

myListView.getChildCount()

可得到屏幕上能看到几行。

再通过遍历:

myListView.getChildAt(i)

就可以得到各行的ViewGroup。ListView的实现原理是,只保存当前可见的几行视图。并保存在ViewGroup的childView 数组中,上述的两个方法getChildCount和getChildAt实际上是ViewGroup的,ListView并未覆盖。

完整的代码类似这样:

/** 
* 获得ListView的子视图,也即能在屏幕中看到的列表行 迭代删除它们的内容,然后重新生成视图内容 
*/ 
for (int i = 0; i < myListView.getChildCount(); i++) { 
    ViewGroup viewgroup = (ViewGroup) myListView.getChildAt(i); 
    viewgroup.removeAllViews(); 
    generateRowElements( 
            myListView.getPositionForView(viewgroup), viewgroup); 
}

完整源代码包含了详细的注释:

package com.easymorse.list;

import java.util.ArrayList; 
import java.util.Collections; 
import java.util.HashSet; 
import java.util.List; 
import java.util.Observable; 
import java.util.Observer; 
import java.util.Set;

import android.app.Activity; 
import android.os.Bundle; 
import android.view.KeyEvent; 
import android.view.View; 
import android.view.View.OnClickListener; 
import android.view.View.OnKeyListener; 
import android.view.View.OnLongClickListener; 
import android.view.ViewGroup; 
import android.widget.BaseAdapter; 
import android.widget.Button; 
import android.widget.CheckBox; 
import android.widget.CompoundButton; 
import android.widget.CompoundButton.OnCheckedChangeListener; 
import android.widget.ImageView; 
import android.widget.ListView; 
import android.widget.TextView; 
import android.widget.Toast;

/** 
* 演示带CheckBox的ListView 
* 
* @author marshal 
* 
*/ 
public class ListViewDemoActivity extends Activity {

    /** 
     * 自定义的用于观察的对象类,当ListView中的元素监控到长按时通知所有元素显示或者不显示CheckBox以及toolbar 
     * 
     * @author marshal 
     * 
     */ 
    class MyObservable extends Observable { 
        public void toobarStatusChanged(boolean visible) { 
            setChanged(); 
            notifyObservers(visible); 
        } 
    }

    // 每行显示几个图片 
    private static final int ROW_ELEMENTS_SIZE = 5;

    // 本例中的ListView 
    private ListView myListView;

    // checkbox是否可见的标志位 
    private boolean checkItemVisible;

    private ViewGroup toolbar;

    // 存放选中的checkbox条目的图片列表下标 
    private Set<Integer> checkedIds = new HashSet<Integer>();

    // 创建观察对象 
    private MyObservable observable = new MyObservable();

    // 演示用的图片数据集合 
    @SuppressWarnings("serial") 
    private List<Integer> drawables = new ArrayList<Integer>() { 
        { 
            add(R.drawable.defense_mechanism); 
            add(R.drawable.gzorah); 
            add(R.drawable.jay_z_linkin_park); 
            add(R.drawable.korn); 
            add(R.drawable.defense_mechanism); 
            add(R.drawable.gzorah); 
            add(R.drawable.jay_z_linkin_park); 
            add(R.drawable.korn); 
            add(R.drawable.defense_mechanism); 
            add(R.drawable.gzorah); 
            add(R.drawable.jay_z_linkin_park); 
            add(R.drawable.korn); 
            add(R.drawable.defense_mechanism); 
            add(R.drawable.gzorah); 
            add(R.drawable.jay_z_linkin_park); 
            add(R.drawable.korn); 
            add(R.drawable.defense_mechanism); 
            add(R.drawable.gzorah); 
            add(R.drawable.defense_mechanism); 
            add(R.drawable.gzorah); 
            add(R.drawable.jay_z_linkin_park); 
            add(R.drawable.korn); 
            add(R.drawable.defense_mechanism); 
            add(R.drawable.gzorah); 
            add(R.drawable.jay_z_linkin_park); 
            add(R.drawable.korn); 
            add(R.drawable.defense_mechanism); 
            add(R.drawable.gzorah); 
            add(R.drawable.jay_z_linkin_park); 
            add(R.drawable.korn); 
            add(R.drawable.defense_mechanism); 
            add(R.drawable.gzorah); 
            add(R.drawable.jay_z_linkin_park); 
            add(R.drawable.korn); 
        } 
    };

    /** Called when the activity is first created. */ 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.main);

        myListView = (ListView) this.findViewById(R.id.myList); 
        myListView.addFooterView(View.inflate(this, R.layout.footer, null));

        toolbar = (ViewGroup) this.findViewById(R.id.toolbar); 
        this.observable.addObserver(new Observer() { 
            @Override 
            public void update(Observable observable, Object data) { 
                toolbar.setVisibility((Boolean) data ? View.VISIBLE 
                        : View.INVISIBLE); 
            } 
        });

        // 设置选择按钮 
        Button button = (Button) this.findViewById(R.id.addToButton); 
        button.setOnClickListener(new OnClickListener() { 
            @Override 
            public void onClick(View v) { 
                Toast.makeText(ListViewDemoActivity.this, "选择了:" + checkedIds, 
                        1000).show(); 
            } 
        });

        // 设置删除按钮 
        button = (Button) this.findViewById(R.id.removeButton); 
        button.setOnClickListener(new OnClickListener() { 
            @Override 
            public void onClick(View v) { 
                /** 
                 * 这里看起来比较繁琐,实际上是为了防止删错记录 checkedIds是Set,是为了避免重复记录元素下标 
                 * 设置为List是为了排序,而且要设置为倒序,目的是从后面记录往前面记录删不会有错 
                 */ 
                List<Integer> list = new ArrayList<Integer>(checkedIds); 
                Collections.sort(list); 
                Collections.reverse(list);

                for (int i : list) { 
                    drawables.remove(i); 
                }

                checkedIds.clear();

                /** 
                 * 获得ListView的子视图,也即能在屏幕中看到的列表行 迭代删除它们的内容,然后重新生成视图内容 
                 */ 
                for (int i = 0; i < myListView.getChildCount(); i++) { 
                    ViewGroup viewgroup = (ViewGroup) myListView.getChildAt(i); 
                    viewgroup.removeAllViews(); 
                    generateRowElements( 
                            myListView.getPositionForView(viewgroup), viewgroup); 
                } 
            } 
        });

        // 自定义的ListView适配器 
        myListView.setAdapter(new BaseAdapter() {

            @Override 
            public View getView(final int position, View convertView, 
                    ViewGroup parent) { 
                // 创建ListView行的布局 
                ViewGroup layout = (ViewGroup) View.inflate( 
                        ListViewDemoActivity.this, R.layout.row, null); 
                generateRowElements(position, layout); 
                return layout; 
            }

            @Override 
            public long getItemId(int position) { 
                return position; 
            }

            @Override 
            public Object getItem(int position) { 
                return drawables.get(position); 
            }

            @Override 
            public int getCount() { 
                // ceil取大于输入值的最小整数,比如输入3.1,则返回4 
                return (int) Math.ceil(drawables.size() 
                        / (float) ROW_ELEMENTS_SIZE); 
            } 
        });

        myListView.setOnKeyListener(new OnKeyListener() { 
            @Override 
            public boolean onKey(View v, int keyCode, KeyEvent event) { 
                if (keyCode == KeyEvent.KEYCODE_BACK && checkItemVisible) { 
                    checkItemVisible = false; 
                    checkedIds.clear();// 清除选择checkbox的id 
                    observable.toobarStatusChanged(false); 
                    return true; 
                } 
                return false;// 其他情况下不处理,使用系统对回退键的处理,即退出应用 
            } 
        });

    }

    /** 
     * 根据行号(position)生成ListView行的内容 
     * 
     * @param position 
     * @param layout 
     */ 
    private void generateRowElements(final int position, ViewGroup layout) { 
        // 防止删除部分内容后该行已经不存在的情况下报错 
        if (position * ROW_ELEMENTS_SIZE > drawables.size() – 1) { 
            return; 
        } 
        // 从图片列表中选取本行需要的子列表 
        List<Integer> sub = drawables.subList(position * ROW_ELEMENTS_SIZE, 
                Math.min((position + 1) * ROW_ELEMENTS_SIZE, drawables.size()));

        for (int i = 0; i < sub.size(); i++) { 
            // 创建元素的视图 
            final View view = View.inflate(ListViewDemoActivity.this, 
                    R.layout.element, null); 
            Integer id = sub.get(i);

            // 图片列表的下标 
            final Integer index = position * ROW_ELEMENTS_SIZE + i;

            // 获取布局中的ImageView,然后赋值图片 
            ImageView imageView = (ImageView) view.findViewById(R.id.cdImage); 
            imageView.setImageResource(id);

            // 获取TextView,然后赋值文字 
            TextView textView = (TextView) view.findViewById(R.id.cdTitle); 
            textView.setText("柴可夫司机");

            // 获取Checkbox 
            final CheckBox checkBox = (CheckBox) view 
                    .findViewById(R.id.checkItem); 
            // 如果在选择列表中有,设置为选取 
            checkBox.setChecked(checkedIds.contains(index));

            // 创建观察者,用于监控是否有Checkbox可见性事件,然后加入到可观察对象中 
            Observer observer = new Observer() { 
                @Override 
                public void update(Observable observable, Object data) { 
                    checkBox.setVisibility((Boolean) data ? View.VISIBLE 
                            : View.INVISIBLE); 
                    // toolbar.setVisibility((Boolean) data ? View.VISIBLE 
                    // : View.INVISIBLE); 
                    checkBox.setChecked(checkedIds.contains(index)); 
                } 
            }; 
            observable.addObserver(observer);

            // checkbox增加监听器,监控勾选状态 
            checkBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {

                @Override 
                public void onCheckedChanged(CompoundButton buttonView, 
                        boolean isChecked) { 
                    if (isChecked) { 
                        checkedIds.add(index); 
                    } else { 
                        checkedIds.remove(index); 
                    } 
                } 
            });

            // 设置checkbox的可见性 
            if (checkItemVisible) { 
                checkBox.setVisibility(View.VISIBLE); 
            }

            // 设置长按监听器 
            view.setOnLongClickListener(new OnLongClickListener() { 
                @Override 
                public boolean onLongClick(View v) { 
                    checkItemVisible = !checkItemVisible; 
                    observable.toobarStatusChanged(checkItemVisible); 
                    return true; 
                } 
            });

            layout.addView(view); 
        } 
    } 
}

完整项目代码见:

http://easymorse.googlecode.com/svn/tags/ListViewDemo-0.4/

举报
华宰
发帖于6年前 0回/3K+阅
顶部