編輯:關於Android編程
上篇文章對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提供按照哪個字段分組就行了。
自然而然的我們就想到了接口,只要實現了相應的接口,接口方法返回需要“分組排序的值”。
數據處理做相應改變即可。
首先抽出以下接口:
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(); }
/** * @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 ArrayListgenSortedDataAndTagLocation(List extends BoSort> myData, HashMap tagLocation) throws InstantiationException, IllegalAccessException { ArrayList res = new ArrayList (); res.addAll(myData); //首先排序 Collections.sort(res, new Comparator () { @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'; } }
Adptor的實現和之前一樣,只是adaptor也是只依賴於接口,不依賴於具體的Bo。
構造一個通用的抽象activity。當需要分組導航的話,只需要繼承之,並實現其中的返回數據的方法即可。
首先把右邊的字母索引抽出來,做成一個View.
單獨的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(); }
一個抽象的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 ListdealedList; private HashMap tagLocation = new HashMap (); /** * */ @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 { @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(); } } }
最近無意中和同事交流數據安全傳輸的問題,想起自己曾經使用過的Rsa非對稱加密算法,閒下來總結一下。什麼是Rsa加密?RSA算法是最流行的公鑰密碼算法,使用長度可以變化的密
這裡記錄一下Canvas 相關API的使用,權當自己作筆記,以後需要好參考前面有一文Android應用程序窗口View的draw過程講到View的繪制過程,其中說到,Vi
Android事件構成在Android中,事件主要包括點按、長按、拖拽、滑動等,點按又包括單擊和雙擊,另外還包括單指操作和多指操作。所有這些都構成了Andro
shape的屬性有:- corners–圓角- padding–內邊距- size(height、width)–設置寬高- strok