android之ListView分组及字母索引导航(2)重构-接口

上篇文章对listView 分组和字母索引导航的实现思路做了分析,并依照思路一步步实现,到最后已经较好的实现了全部功能。但是仔细研究就会发现其实现不够好,主要问题:

1.               对于一个使用范围比较广泛的布局,以上实现不够通用,尤其是Bo中需加上一些多余的字段,这些字字段本身并没有意义。

2.               代码都糅合在activity中。

针对以上两点做一些代码重构。首先我们把其优化为一个通用的activity.这样做成通用的View就很容易;然后对代码进行抽取和重构。

想法和思路

以往代码的一个主要问题就是“污染”原有的Bo,而污染的主要原因是需要用这些附加的字段来进行数据处理和生成列表分组标签的时候使用。

原有代码如下:

public class TestBo {

    /**
     * 主要字段
     */
    private String boStr = null;

    /**
     * bo拼音缓存
     */
    private String boPinYin = null;
    /**
     * Bo标签标记
     */
    private String boTagFlag = null;

    public TestBo() {
       super();
    }

    public TestBo(String str) {
       super();
       this.boStr = str;
    }

    public String getBoStr() {
       return boStr;
    }

    public void setBoStr(String boStr) {
       this.boStr = boStr;
    }

    public String getSortStrPinyin() {
       return boPinYin;
    }

    public void setSortStrPinYin(String pinyin) {
       this.boPinYin = pinyin;
    }

    public void setTag(String tag) {
       this.boTagFlag = tag;
    }
    public String getTag() {
       return boTagFlag;
    }
}

其实以上Bo中真正有用的有主要字段,其他均为附加字段,其实生成列表只要要求Bo提供按照哪个字段分组就行了。

自然而然的我们就想到了接口,只要实现了相应的接口,接口方法返回需要“分组排序的值”。

数据处理做相应改变即可。

重构BO-接口

首先抽出以下接口:

public interface BoSort  {

    /**
    * @date 2014-9-3
    * @Description: 获取索引的字符串
    * @param
    * @return String
    */
    public String getSortStr();
    /**
    * @date 2014-9-3
    * @Description: 获取索引字符串的拼音,这个最好可以有缓存
    * @param
    * @return String
    */
    public String getSortStrPinyin();

    /**
    * @date 2014-9-3
    * @Description:
    * @param
    * @return void
    */
    public void setSortStrPinYin(String pinyin);
    /**
    * @date 2014-9-3
    * @Description: 设置标签,需要缓存
    * @param
    * @return void
    */
    public void setTag(String tag);
    /**
    * @date 2014-9-3
    * @Description: 获取标签,如果为null,说明不是标签
    * @param
    * @return String
    */
    public String getTag();
}

相应Bo实现以上接口即可。但是我们可以提供一个默认实现,这还是一个抽象类,Bo只要继承这个默认实现,并实现为实现的方法public String getSortStr();

/**
 * @date 2014-9-3
 * @Description: 只需实现 getSortStr 其他不要修改

 */
public  abstract class DefaultBoSortImp implements BoSort{

    /**
     * bo拼音缓存
     */
    private String boPinYin = null;
    /**
     * Bo标签标记
     */
    private String boTagFlag = null;

    /**
     * 一定要有这个构造函数
     */
    public DefaultBoSortImp() {
       super();
    }

    @Override
    public String getSortStrPinyin() {
       return boPinYin;
    }

    @Override
    public void setSortStrPinYin(String pinyin) {
       this.boPinYin = pinyin;
    }

    @Override
    public void setTag(String tag) {
       this.boTagFlag = tag;
    }
    @Override
    public String getTag() {
       return boTagFlag;
    }
}

数据处理

整体的实现过程和以前类似,数据处理的时候稍微有些改变。我们把数据处理单独抽为一个类,可见处理的过程中,生成分组标签的时候,采用反射,且此数据处理只依赖与接口,而不是具体的Bo,降低了耦合。

public class RulerUtil {

    /**
     * 列表适配??
     */
    public static final String[] indexStr = { "#", "A", "B", "C", "D", "E", "F", "G", "H",
            "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U",
            "V", "W", "X", "Y", "Z" };
    public static final char[] letters = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
            'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
            'V', 'W', 'X', 'Y', 'Z' };

    /**
     * @throws IllegalAccessException
     * @throws InstantiationException
     * @return返回处理后的数据
     * @Description:处理数据,排序,添加标签
     */
    public  static ArrayList<BoSort> genSortedDataAndTagLocation(List<? extends BoSort> myData, HashMap<String, Integer> tagLocation) throws InstantiationException, IllegalAccessException {

       ArrayList<BoSort> res = new ArrayList<BoSort>();
       res.addAll(myData);
       //首先排序
       Collections.sort(res, new Comparator<BoSort>() {
           @Override
           public int compare(BoSort lhs, BoSort rhs) {
              char firChar = checkAndGetPinyin(lhs);
              char secChar = checkAndGetPinyin(rhs);
              if (firChar < secChar) {
                  return -1;
              } else if (firChar > secChar) {
                  return 1;
              } else
                  return 0;
           }
       });

       int size = res.size();
       int i = 0;
       char nowTag = '\0';

       for (i = 0; i < size; i++) {
           BoSort temp = res.get(i);
           char tempTag = checkAndGetPinyin(temp);
           if(Arrays.binarySearch(letters, tempTag) < 0){
              tempTag = '#';
           }
           if (nowTag != tempTag) {
              //反射生成标签
              Class<? extends BoSort> boClass = temp.getClass();
              BoSort tagBO = boClass.newInstance();
              tagBO.setTag(tempTag+"");
              res.add(i, tagBO);
              tagLocation.put(tempTag + "", i);
              i++;
              size++;
              nowTag = tempTag;
           }
       }
       tagLocation.put("#", 0);
       return res;
    }

    private static char checkAndGetPinyin(BoSort bo){
       String pinyinStr = bo.getSortStrPinyin();
       if (pinyinStr==null) {
           bo.setSortStrPinYin(HanziToPinyin.getPinYin(bo.getSortStr()).toUpperCase());
           pinyinStr = bo.getSortStrPinyin();
       }
       if(pinyinStr!=null&&pinyinStr.length()>=1){
           return pinyinStr.charAt(0);
       }
       return '\0';
    }
}

Adaptor实现

Adptor的实现和之前一样,只是adaptor也是只依赖于接口,不依赖于具体的Bo。

Activity的重构

构造一个通用的抽象activity。当需要分组导航的话,只需要继承之,并实现其中的返回数据的方法即可。

首先把右边的字母索引抽出来,做成一个View.

1.   RulerWidget

单独的View,可以直接在xml布局中使用。

/**
 * @Description: 右边尺子导航,需要调用  setOnRulerTouch方法设置回调接口
 */
public class RulerWidget extends LinearLayout{

    private static final int INDEX_LENGTH = RulerUtil.indexStr.length;

    public RulerWidget(Context context) {
       super(context);
       init();
    }

    public RulerWidget(Context context, AttributeSet attrs) {
       super(context, attrs);
       init();
    }
    public RulerWidget(Context context, AttributeSet attrs, int defStyle) {
       super(context, attrs, defStyle);
       init();
    }

    private void init(){
       int color = getResources().getColor(R.color.g_ruler_letter_color);
       LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
       params.gravity = Gravity.CENTER_HORIZONTAL;
       this.bringToFront();

       params.weight = 1;
       for (int i = 0; i < RulerUtil.indexStr.length; i++) {
           final TextView tv = new TextView(getContext());
           tv.setLayoutParams(params);
           tv.setTextColor(color);
           tv.setGravity(Gravity.CENTER);
           tv.setText(RulerUtil.indexStr[i]);
           this.addView(tv);
       }
       this.setOnTouchListener(new OnTouchListener() {

           @Override
           public boolean onTouch(View v, MotionEvent event) {
              int height = v.getHeight();
              float pos = event.getY();
              int sectionPosition = (int) ((pos / height) / (1f /INDEX_LENGTH));
              if (sectionPosition < 0) {
                  sectionPosition = 0;
              } else if (sectionPosition > INDEX_LENGTH-1) {
                  sectionPosition = INDEX_LENGTH-1;
              }

              switch (event.getAction()) {
              case MotionEvent.ACTION_DOWN:
                  if (onRulerTouch!=null) {
                     onRulerTouch.onDown(sectionPosition);
                  }
                  RulerWidget.this.setBackgroundResource(R.color.g_ruler_selected);
                  break;
              case MotionEvent.ACTION_MOVE:
                  if (onRulerTouch!=null) {
                     onRulerTouch.onMove(sectionPosition);
                  }
                  break;
              default:
                  if (onRulerTouch!=null) {
                     onRulerTouch.onOthers();
                  }
                  RulerWidget.this.setBackgroundResource(R.color.g_blank);
              }
              return true;
           }
       });
    }
    /**
     * 回调
     */
    private OnRulerTouch onRulerTouch;

    public void setOnRulerTouch(OnRulerTouch onRulerTouch) {
       this.onRulerTouch = onRulerTouch;
    }

}
/**
 * @date 2014-9-3
 * @Description: ruler触摸回调
 */
public interface OnRulerTouch{
    public void onDown(int position);
    public void onMove(int position);
    public void onUP();
    public void onOthers();
}

2.   Activity

一个抽象的activity. 布局和以前类似。不在贴。

/**
 * @date 2014-9-3
 * @Description:需要实现这个获取数据的方法
 *    public abstract List<? extends BoSort> getDataList();
 */
public abstract class RulerActivity extends Activity{

    protected TextView noDataView;

    protected TextView RulerTag;
    protected ProgressBarWithText progress;

    protected ListView listView;

    protected RulerWidget ruler;

    private RulerAdapter rulerAdapter;
    private List<? extends BoSort> originalList;
    private List<BoSort> dealedList;
    private HashMap<String, Integer> tagLocation = new HashMap<String, Integer>();

    /**
    *
    */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.g_ruler);
       findView();
       initView();
       initData();
    }

    private void findView() {
       noDataView = (TextView) findViewById(R.id.g_base_list_nodata);
       RulerTag = (TextView) findViewById(R.id.g_ruler_tag);
       progress = (ProgressBarWithText) findViewById(R.id.g_base_progressbar_withtext);
       listView = (ListView) findViewById(R.id.g_base_list);
       ruler = (RulerWidget) findViewById(R.id.g_ruler);
    }
    private void initView() {
       progress.setVisibility(View.VISIBLE);
       RulerTag.setVisibility(View.GONE);
       noDataView.setVisibility(View.GONE);
       listView.setVisibility(View.GONE);
       ruler.setVisibility(View.GONE);
    }
    private void initData() {
       new GetDataAsyTask().execute();
    }

    /**
    * @date 2014-9-4
    * @Description: 需要实现这个获取数据的方法
    * @param
    * @return List<? extends BoSort>
    */
    public abstract List<? extends BoSort> getDataList();

    /**
    * @date 2014-9-3
    * @Description:
    * @param
    * @return void
    */
    private void handleSuccessData() {
       listView.setVisibility(View.VISIBLE);
       ruler.setVisibility(View.VISIBLE);
       rulerAdapter = new RulerAdapter(dealedList, this);
       ruler.setOnRulerTouch(new OnRulerTouch() {
           @Override
           public void onUP() {
           }
           @Override
           public void onOthers() {
              RulerTag.setVisibility(View.GONE);
           }
           @Override
           public void onMove(int position) {
              RulerTag.setText(RulerUtil.indexStr[position]);
              listView.setSelection(getPosition(position));
           }
           @Override
           public void onDown(int position) {
              RulerTag.setVisibility(View.VISIBLE);
              RulerTag.setText(RulerUtil.indexStr[position]);
              listView.setSelection(getPosition(position));
           }
       });

       listView.setAdapter(rulerAdapter);
       rulerAdapter.notifyDataSetChanged();

    }

    /**
     * @Description: 获取触摸字母导航的时候,列表要滚动到的位置。如果触摸的字母,在标签tagLocation 映射中,不存,则向前寻找。
     */
    private Integer getPosition(final int j) {
       Integer pos = null;
       int i = j;
       while (pos == null && i <= RulerUtil.indexStr.length - 1) {
           pos = tagLocation.get(RulerUtil.indexStr[i]);
           i++;
       }
       if (pos == null) {
           pos = dealedList.size() - 1;
       }
       return pos;
    }
    class GetDataAsyTask extends AsyncTask<Void, Void, Void> {

       @Override
       protected void onPreExecute() {
           super.onPreExecute();
           progress.setVisibility(View.VISIBLE);
       }

       @Override
       protected Void doInBackground(Void... params) {
           originalList = getDataList();
           try {
              dealedList = RulerUtil.genSortedDataAndTagLocation(originalList, tagLocation);
           } catch (Exception e) {
              e.printStackTrace();
              if (dealedList!=null) {
                  dealedList.clear();
                  dealedList = null;
              }

              if (originalList!=null) {
                  originalList.clear();
                  originalList = null;
              }
              if (tagLocation!=null) {
                  tagLocation.clear();
                  tagLocation = null;
              }
           }
           return null;
       }

       @Override
       protected void onPostExecute(Void result) {
           progress.setVisibility(View.GONE);
           super.onPostExecute(result);
           if(dealedList==null){
              noDataView.setVisibility(View.VISIBLE);
              return;
           }
           handleSuccessData();
       }
    }

}

至此重构完成。

时间: 2024-08-28 13:54:50

android之ListView分组及字母索引导航(2)重构-接口的相关文章

Android 实现ListView的A-Z字母排序和过滤搜索功能,实现汉字转成拼音

转载请注明出处:http://blog.csdn.net/xiaanming/article/details/12684155 前段时间因为换工作的缘故又恰巧碰到国庆节,所以有段时间自己没有更新博客了,过完国庆到新公司报道,感觉还不错,就是现在住的地方离新公司有点远,地铁20站,伤不起啊,我每天早上7点多就要起床,然后屁颠屁颠的去挤地铁上班,晚上下班还要挤地铁,先不说路程远,车费一天就要10几块,我的银子啊,有坐龙华线去上班的深圳程序员不?听说那条线上班高峰期很挤?我没在上班高峰期坐过那趟车,我

一步一步实现字母索引导航栏

先来看下实现后的效果: DEMO 链接:在线DEMO,源代码 这个索引导航栏的效果在很多 APP 中都有应用,我也是参考了一些 APP 的效果进行实现. 不过之前接触移动端页面开发较少,所以是边学边做,也就把这个过程中的一些东西整理记录下来. 设计 这个功能的基本需求可以总结为一句话:手指在导航栏(也就是 DEMO 上页面右侧的包含字母的竖条)拖动时,根据当前手指位置,页面主体内容列表跳转到对应字母的内容项. 当然,延伸开来,可以是对于已经排序的列表,导航栏显示对应的索引字符列表,支持快速跳转到

Android 自定义 View 实现通讯录字母索引(仿微信通讯录)

一.效果:我们看到很多软件的通讯录在右侧都有一个字母索引功能,像微信,小米通讯录,QQ,还有美团选择地区等等.这里我截了一张美团选择城市的图片来看看: 我们今天就来实现图片中右侧模块的索引功能,包括触摸显示以选中的索引字母.这里我的UI界面主要是参照微信的界面来实现,所以各位也可以对照微信来看看效果,什么都不说了,只有效果图最具有说服力! 二.分析: 我们看到这样的效果我们心理都回去琢磨,他是如何实现的: 首先,它肯定是通过自定义 View 来实现的,因为 Android 没有提供类似这样的控件

android的listview分组显示的时候layout_marginTop失效的解决办法

在使用android的ListView组件做类似于通讯录这样的功能时,需要根据A.B.C这样的标题来区来分组显示通讯录中的姓名,本人在做实验过程中遇到了这样一个问题,比如,想让标题item和上边的用户名item中间有个间隙,而组(同个标题下)用户名之间不能有间隙,原本以为很简单,我认为在标题item的最外层LinearLayout中增加一个layout_marginTop属性即可,结果发现一只无效,最后请教了个高手告诉我需要在标题item的顶层LinearLayout中再嵌套一层,然后在第二层中

android列表分组及字母导航-重构(2)-注解

上篇文章对listView 分组和字母索引导航进行了重构,重构之后,使新的实现只依赖于接口,而不是具体的Bo.但是还是要求原始的数据Bo实现接口或者继承抽象类.能不能把这一步也简化呢,只需要原始的数据Bolist? 答案是可以的,可以使用注解,也就是annnotion. 想法和思路 Java注解又叫java标注,java提供了一套机制,使得我们可以对方法.类.参数.包.域以及变量等添加标准(即附上某些信息).且在以后某个时段通过反射将标注的信息提取出来以供使用. 分组listView 在上次重构

[Android分享] 【转帖】Android ListView的A-Z字母排序和过滤搜索功能

感谢eoe社区的分享 最近看关于Android实现ListView的功能问题,一直都是小伙伴们关心探讨的Android开发问题之一,今天看到有关ListView实现A-Z字母排序和过滤搜索功能并且实现汉字转成拼音的功能,转帖来和eoe的小伙伴们一同分享下! Android 有关ListView实现A-Z字母排序和过滤搜索功能并且实现汉字转成拼音的功能 我们知道一般我们对联系人,城市列表等实现A-Z的排序,因为联系人和城市列表我们可以直接从数据库中获取他的汉字拼音,而对于一般的数据,我们怎么实现A

Anroid ListView分组和悬浮Header实现

Anroid ListView分组和悬浮Header实现 分类: Android2014-01-27 12:26 6585人阅读 评论(13) 收藏 举报 listviewheadersection分组悬浮 目录(?)[+] 之前在使用iOS时,看到过一种分组的View,每一组都有一个Header,在上下滑动的时候,会有一个悬浮的Header,这种体验觉得很不错,请看下图: 上图中标红的1,2,3,4四张图中,当向上滑动时,仔细观察灰色条的Header变化,当第二组向上滑动时,会把第一组的悬浮H

自定义控件之android列表分组及字母导航

有了以上两篇文章的重构,现在把ListView分组列表重构为自定义控件就会非常简单,只需要把初始化操作放在自定义控件的构造函数里面.重构后的自定义控件以上一篇的注解重构为基础. 基本结构 这里首先贴上一张上篇文章重构后的activity的代码结构,相关的方法实现在之前两篇文章中都有贴出. 再贴一张重构后的View的结构.可见两者的结构都及其相似.不同的是上边的activity中有抽象方法getDataList(),而下边的没有,但是多了一个ILoadRulerData<T> iLoadData

一、ListView的侧边字母滑动索引

一.最终效果 二.功能分析与实现 1.LisetView布局 分析:同样的字母开头,第一个上方有该字母的标志 实现:在item布局中除了TextView再在其上方加一个TextView等布局(用于显示数据的首字母),然后在适配器的getView中判断该布局是否显示.默认设置该布局的Visibility为VISIBLE,当该布局的的文字内容首字母与上一个不同时说明该item是新的首字母开头,设置为VISIBLE,否则设置为GONE. 部分代码: // 默认显示 为了显示第一个布局以及布局复用出现的