編輯:關於Android編程
那麼我們就開始吧
第一,首先我們來解決自定義View實現浮動的字母索引項的列表.
package com.mikyou.contactdemo.myview; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Typeface; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; public class MikyouLetterListView extends View { private OnTouchingLetterChangedListener listener; //定義了顯示在最右邊的浮動的索引項的列表,當然這個是固定的,所以可以直接初始化,如果需要變動的話則可以通過自定義屬性來指定 String[] b = { "#","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"}; int choose = -1;//用於標記點擊存放字母數組中的下標 Paint paint = new Paint(); boolean showBkg = false;//這個boolean變量主要是控制當我們點擊索引列表中整個索引列表的背景有個變化,為false表示開始沒點擊背景為正常顯示時候的背景 public MikyouLetterListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public MikyouLetterListView(Context context, AttributeSet attrs) { super(context, attrs); } public MikyouLetterListView(Context context) { super(context); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (showBkg) {//如果此時為true的話則表示需要改變整個canvas背景也即是索引項的背景 canvas.drawColor(Color.parseColor("#10000000")); } /** * 注意:在自定義view中的如果不需要設置wrap_content屬性就不需要自己重寫onMeasure方法 * 因為在onMeasure方法中系統默認會自動測量兩種模式:一個是match_parent另一個則是自己指定明確尺寸大小 * 這兩種方法對應著這一種MeasureSpec.AT_MOST測量模式,由於我們設置這個自定義浮動的字母索引表寬度是指定明確大小 * 高度是match_parent模式,所以這裡就不要手動測量了直接通過getHeight和getWidth直接拿到系統自動測量好高度和寬度 * */ int height = getHeight(); int width = getWidth(); //讓整個顯示的每個字母均分整個屏幕高度尺寸,這個singleHeight就是每個字母占據的高度 int singleHeight = height / b.length; //遍歷循環繪制每一個字母text for (int i = 0; i < b.length; i++) { //繪制字母text的顏色 paint.setColor(Color.parseColor("#515151")); //繪制字母text的字體大小 paint.setTextSize(25); //繪制字母text的字體樣式 paint.setTypeface(Typeface.DEFAULT_BOLD); //設置抗鋸齒樣式 paint.setAntiAlias(true); if (i == choose) {//判斷如果點擊字母的下標等於i,那麼就會設置繪制點擊字母的樣式用於高亮顯示 paint.setColor(Color.parseColor("#3399ff")); paint.setFakeBoldText(true); } /** * 注意:canvas在繪制text的時候,他繪制的起點不是text的左上角而是它的左下角 * (xPos,yPos)表示每個字母左下角的位置的坐標 *xPos = width / 2 - paint.measureText(b[i]) / 2:意思很容易理解,就是用 * (總的view的寬度(可能還包括leftPadding和rightPadding的大小)-每個字母寬度)/2得到就是每個字母的左下角的X坐標, * 仔細想下每個text的起點的x坐標都是一樣的.paint.measureText(b[i])得到每一個字母寬度大小 * 由於是左下角,所以它們的Y坐標:應該如下設置 yPos = singleHeight * i + singleHeight; * */ float xPos = width / 2 - paint.measureText(b[i]) / 2;//得到繪制字母text的起點的X坐標 float yPos = singleHeight * i + singleHeight;//得到繪制字母text的起點的Y坐標 canvas.drawText(b[i], xPos, yPos, paint);//開始繪制每個字母 paint.reset();//繪制完一個字母需要重置一下畫筆對象 } } @Override public boolean dispatchTouchEvent(MotionEvent event) {//重寫view的觸摸事件分發方法 final int action = event.getAction(); final float y = event.getY();//由於只涉及到Y軸坐標,只獲取y坐標 final int oldChoose = choose;//oldChoose用於記錄上一次點擊字母所在字母數組中的下標 final int c = (int) (y / getHeight() * b.length);//得到點擊或觸摸的位置從而確定對應點擊或觸摸的字母所在字母數組中的下標 switch (action) { case MotionEvent.ACTION_DOWN://監聽按下事件 showBkg = true;//按下後整個view的背景變色,showBkg為true if (oldChoose != c && listener != null) {//如果此次點擊的字母數組下標不等於上一次的且已經注冊了監聽事件的, if (c >= 0 && c <= b.length) {//並且點擊得到數組下標在字母數組范圍內的,我們就將此時的字母回調出去 listener.onTouchingLetterChanged(b[c]);//我們就將此時的對應在字母數組中的字母回調出去 choose = c;//並且更新當前選中的字母下標存儲在choose變量中 invalidate();//最後通知canvas重新繪制 } } break; case MotionEvent.ACTION_MOVE://監聽移動事件,因為按下的時候已經把背景showBkg設置true,這裡就不需要重新設置,其他操作與按下的事件一致 if (oldChoose != c && listener != null) { if (c >= 0 && c <= b.length) { listener.onTouchingLetterChanged(b[c]); choose = c; invalidate(); } } break; case MotionEvent.ACTION_UP://監聽手指抬起的動作 showBkg = false;//此時的背景將會恢復到初始狀態,showBkg=false choose = -1;//此時記錄下標的變量也需要重置 invalidate();//並且重繪整個view break; } return true; } @Override public boolean onTouchEvent(MotionEvent event) { return super.onTouchEvent(event); } /** * 注冊自定義監聽器 * */ public void setOnTouchingLetterChangedListener( OnTouchingLetterChangedListener listener) { this.listener = listener; } /** * 定義一個接口,用於回調出點擊後的字母,顯示在彈出的字母對話框中 * */ public interface OnTouchingLetterChangedListener { public void onTouchingLetterChanged(String s); } }第二,點擊字母後彈出的字母框的布局和樣式的實現(這個比較簡單彈出框就是一個TextView控件):
overlay.xml(布局)overlay_bg.xml(布局樣式):
package com.mikyou.myguardian.bean; import java.io.Serializable; /** * Created by mikyou on 16-7-19. */ public class ContactBean implements Serializable { private int iconId; private String title; private String phoneNum; private String firstHeadLetter; public ContactBean(int iconId, String title, String phoneNum, String firstHeadLetter) { this.iconId = iconId; this.title = title; this.phoneNum = phoneNum; this.firstHeadLetter=firstHeadLetter; } public ContactBean() { } public int getIconId() { return iconId; } public String getFirstHeadLetter() { return firstHeadLetter; } public void setFirstHeadLetter(String firstHeadLetter) { this.firstHeadLetter = firstHeadLetter; } public void setIconId(int iconId) { this.iconId = iconId; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getPhoneNum() { return phoneNum; } public void setPhoneNum(String phoneNum) { this.phoneNum = phoneNum; } @Override public String toString() { return "ContactBean{" + "iconId=" + iconId + ", title='" + title + '\'' + ", phoneNum='" + phoneNum + '\'' + ", descriptor='" + descriptor + '\'' + ", firstHeadLetter='" + firstHeadLetter + '\'' + ", headLetterNum='" + headLetterNum + '\'' + '}'; } }第四整個布局activity_main.xml:
package com.mikyou.myguardian.service; import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; import android.net.Uri; import com.mikyou.myguardian.bean.ContactBean; import java.util.ArrayList; import java.util.List; /** * Created by mikyou on 16-7-19. * 用於返回讀取到聯系人的集合 */ public class ContactInfoService { private Context context; public ContactInfoService(Context context) { this.context = context; } public List第六,就是實現聯系人的ListView大家可能會看到這個和我們平時的看到的ListView有些不一樣,因為在此次聯系人的ListView中還有"A","B","C","D"...這小的字母item這個主要是將相同聯系人的第一個字的拼音的首字母放在一起。那怎麼去實現這樣的一個ListView呢?getContactList(){ List mContactBeanList=new ArrayList<>(); ContactBean mContactBean=null; ContentResolver mContentResolver=context.getContentResolver(); Uri uri=Uri.parse("content://com.android.contacts/raw_contacts"); Uri dataUri=Uri.parse("content://com.android.contacts/data"); Cursor cursor =mContentResolver.query(uri,null,null,null,null); while (cursor.moveToNext()){ mContactBean=new ContactBean(); String id=cursor.getString(cursor.getColumnIndex("_id")); String title=cursor.getString(cursor.getColumnIndex("display_name"));//獲取聯系人姓名 String firstHeadLetter=cursor.getString(cursor.getColumnIndex("phonebook_label"));//這個字段保存了每個聯系人首字的拼音的首字母 mContactBean.setTitle(title); mContactBean.setFirstHeadLetter(firstHeadLetter); Cursor dataCursor=mContentResolver.query(dataUri,null,"raw_contact_id= ?",new String[]{id},null); while(dataCursor.moveToNext()){ String type=dataCursor.getString(dataCursor.getColumnIndex("mimetype")); if (type.equals("vnd.android.cursor.item/phone_v2")){//如果得到的mimeType類型為手機號碼類型才去接收 String phoneNum=dataCursor.getString(dataCursor.getColumnIndex("data1"));//獲取手機號碼 mContactBean.setPhoneNum(phoneNum); } } dataCursor.close(); if (mContactBean.getTitle()!=null&&mContactBean.getPhoneNum()!=null){ mContactBeanList.add(mContactBean); } } cursor.close(); return mContactBeanList; } }
具體看該ListView的Adapter: package com.mikyou.myguardian.adapter; import android.content.Context; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import com.mikyou.adapter.CommonAdapter; import com.mikyou.myguardian.R; import com.mikyou.myguardian.bean.ContactBean; import com.mikyou.tools.ViewHolder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Created by mikyou on 16-7-19. */ public class TestContactListAdapter extends CommonAdapter第七該ListView的Item布局:{ private final int VIEW_TYPE=3; private Map alphaIndexer; private List sections; private List listBeans; private OnGetAlphaIndexerAndSectionsListener listener; private boolean flag;//標志用於只執行一次代碼 public TestContactListAdapter(Context context, List listBeans, int layoutId) { super(context, listBeans, layoutId); this.listBeans=listBeans; alphaIndexer=new HashMap<>(); sections=new ArrayList<>(); for (int i = 0; i =0?listBeans.get(i-1).getFirstHeadLetter():""; if (!previewAlpha.equals(currentAlpha)){ String firstAlpha=listBeans.get(i).getFirstHeadLetter(); alphaIndexer.put(firstAlpha,i); sections.add(firstAlpha); } } } @Override public int getItemViewType(int position) { int type=0; if (position==0){ type=2; }else if (position==1){ type=1; } return type; } @Override public int getCount() { //注意:為什麼沒有直接把回調方法的調用寫在構造器中呢?因為構造器只會調用一次,當第一次調用listener的時候是為空的 //而要初始化listener對象,則需要先去創建對象再去通過對象調用set方法來初始化這個listener對象,再去new對象的時候又要去用到listener產生了矛盾 //所以放在getCount中調用,只會調用一次,符合需求 if (!flag){ if (listener!=null){ listener.getAlphaIndexerAndSectionsListner(alphaIndexer,sections); } flag=true; } return listBeans.size(); } @Override public int getViewTypeCount() { return VIEW_TYPE; } @Override public void convert(ViewHolder viewHolder, ContactBean contactBean) { int viewType=getItemViewType(viewHolder.getmPosition()); ImageView iv=viewHolder.getView(R.id.contact_icon_id); iv.setImageResource(R.mipmap.contact_user); viewHolder.setText(R.id.contact_title,contactBean.getTitle()).setText(R.id.contact_phone_num,contactBean.getPhoneNum()); if (viewHolder.getmPosition()>=1){ String currentAlpha=listBeans.get(viewHolder.getmPosition()).getFirstHeadLetter(); String previewAlpha=listBeans.get(viewHolder.getmPosition()-1).getFirstHeadLetter(); if (!previewAlpha.equals(currentAlpha)){//不相等表示有新的字母項產生且為該類字母堆中的第一個字母索引項 viewHolder.getView(R.id.first_alpha).setVisibility(View.VISIBLE);//把新的字母列表項設置VISIBlE TextView tv= viewHolder.getView(R.id.first_alpha); tv.setText(currentAlpha); }else {//表示沒有新的字母堆出現,也就說明該item是屬於同類字母堆中且不是第一個,那麼就需要將這個索引項設置GONE viewHolder.getView(R.id.first_alpha).setVisibility(View.GONE); } } } public void setOnGetAlphaIndeserAndSectionListener(OnGetAlphaIndexerAndSectionsListener listener){ this.listener=listener; } public interface OnGetAlphaIndexerAndSectionsListener{ public void getAlphaIndexerAndSectionsListner(Map alphaIndexer,List sections); } }
package com.mikyou.contactdemo; import android.content.Context; import android.graphics.PixelFormat; import android.os.Bundle; import android.os.Handler; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.ListView; import android.widget.TextView; import com.mikyou.contactdemo.adapter.TestContactListAdapter; import com.mikyou.contactdemo.bean.ContactBean; import com.mikyou.contactdemo.myview.MikyouLetterListView; import com.mikyou.contactdemo.service.ContactInfoService; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; public class MainActivity extends AppCompatActivity implements TestContactListAdapter.OnGetAlphaIndexerAndSectionsListener{ private List到這裡就談得差不多了,這個很常用,准備給自己以後的項目中繼續用。mContactBeanList;//所有聯系人集合 private ListView mContactListView;//聯系人ListView private MikyouLetterListView mLetterListView;//字母表 private TextView overLayout;//彈出對話框 private OverlayThread overlayThread; private Map alphaIndexer;// 存放存在的漢語拼音首字母和與之對應的列表位置 private Handler handler; private TestContactListAdapter adapter; private List sections;// 存放存在的漢語拼音首字母 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initData(); initView(); initOverlay(); } private void initView() { registerAllViewIds(); registerAllViewAdapters(); registerAllViewEvents(); } private void registerAllViewIds() { mContactListView= (ListView) findViewById(R.id.id_listview); mLetterListView= (MikyouLetterListView) findViewById(R.id.id_letterview); } private void registerAllViewAdapters() { adapter=new TestContactListAdapter(this,mContactBeanList,R.layout.contact_list_item); adapter.setOnGetAlphaIndeserAndSectionListener(this); mContactListView.setAdapter(adapter); } private void registerAllViewEvents() { mLetterListView.setOnTouchingLetterChangedListener(new LetterListViewListener()); } private void initData() { //alphaIndexer=new HashMap<>(); handler=new Handler(); overlayThread=new OverlayThread(); ContactInfoService mContactInfoService=new ContactInfoService(this); mContactBeanList=mContactInfoService.getContactList();//返回手機聯系人對象集合 //按拼音首字母表排序 Collections.sort(mContactBeanList,comparator); } @Override public void getAlphaIndexerAndSectionsListner(Map alphaIndexer, List sections) { this.alphaIndexer=alphaIndexer; this.sections=sections; Log.d("list",alphaIndexer.toString()+"\n"+sections.toString()); } /** * @Mikyou * 字母列表點擊滑動監聽器事件 * */ private class LetterListViewListener implements MikyouLetterListView.OnTouchingLetterChangedListener { @Override public void onTouchingLetterChanged(final String s) { if (alphaIndexer.get(s) != null) {//判斷當前選中的字母是否存在集合中 int position = alphaIndexer.get(s);//如果存在集合中則取出集合中該字母對應所在的位置,再利用對應的setSelection,就可以實現點擊選中相應字母,然後聯系人就會定位到相應的位置 mContactListView.setSelection(position); overLayout.setText(s); overLayout.setVisibility(View.VISIBLE); handler.removeCallbacks(overlayThread); // 延遲一秒後執行,讓overlay為不可見 handler.postDelayed(overlayThread, 1500); } } } /** * @mikyou * 初始化漢語拼音首字母彈出提示框 * */ private void initOverlay() { LayoutInflater inflater = LayoutInflater.from(this); overLayout = (TextView) inflater.inflate(R.layout.overlay, null); overLayout.setVisibility(View.INVISIBLE); WindowManager.LayoutParams lp = new WindowManager.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_APPLICATION, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT); WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE); windowManager.addView(overLayout, lp); } /** * @Mikyou * 首字母按a-z排序 * */ Comparator comparator=new Comparator () { @Override public int compare(ContactBean t1, ContactBean t2) { String a=t1.getFirstHeadLetter(); String b=t2.getFirstHeadLetter(); int flag=a.compareTo(b); if (flag==0){ return a.compareTo(b); }else{ return flag; } } }; /** * @Mikyou * 設置overlay不可見 * */ private class OverlayThread implements Runnable{ @Override public void run() { overLayout.setVisibility(View.GONE); } } }
一.內存洩漏概念1.什麼是內存洩漏?用動態存儲分配函數動態開辟的空間,在使用完畢後未釋放,結果導致一直占據該內存單元。直到程序結束。即所謂的內存洩漏。其實說白了就是該內存
Android中的不同Activity之間傳遞對象,我們可以考慮采用Bundle.putSerializable(Key,Object);也可以考慮采用Bun
目前,各種App的社區或者用戶曬照片、發說說的地方,都提供了評論功能,為了更好地學習,自己把這個功能實現了一下,做了個小的Demo。首先推薦一款實用的插件LayoutCr
現在大家越來越多的使用AndroidStudio進行Android開發,那麼今天就和大家一起交流一下AndroidStudio開發NDK的配置方法。AndroidStud