Android通话记录ListView实现左滑发短信和右滑打电话

本文详细介绍了如何在Android环境下实现通话记录清单的左右滑动功能,并通过自定义组件SlideListView,提供了通话和发送短信的快捷操作,提升了用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

下午公司篮球赛最后一场,超神队一分憾败。可惜,不过比赛归比赛,重要的是过程中的精彩。

通话记录向左滑动发短信,向右滑动打电话,微信电话本上有这个很便捷的操作,在项目的修改过程中,就仿造微信的交互方式,实现了ListView左右滑动,今天总结一下:

同样由于是在源码环境下编译,所以代码中的资源引用之类地方的可能和常规的稍微不同。

好了 ,shut up and show code:

通话记录清单Activity:

[java]  view plain copy
  1. package com.android.dialer.calllog;  
  2.   
  3. import java.text.SimpleDateFormat;  
  4. import java.util.ArrayList;  
  5. import java.util.Date;  
  6. import java.util.List;  
  7.   
  8. import com.android.dialer.DialerApplication;  
  9. import com.android.dialer.DialtactsActivity;  
  10. import com.android.dialer.calllog.RecordSlideListView.*;  
  11.   
  12. import android.R;  
  13. import android.app.Activity;  
  14. import android.app.ProgressDialog;  
  15. import android.content.AsyncQueryHandler;  
  16. import android.content.ComponentName;  
  17. import android.content.ContentResolver;  
  18. import android.content.Intent;  
  19. import android.database.Cursor;  
  20. import android.database.sqlite.SQLiteDatabase;  
  21. import android.graphics.Color;  
  22. import android.net.Uri;  
  23. import android.os.Bundle;  
  24. import android.os.Handler;  
  25. import android.provider.CallLog;  
  26. import android.util.Log;  
  27. import android.view.ContextMenu;  
  28. import android.view.KeyEvent;  
  29. import android.view.MenuInflater;  
  30. import android.view.MenuItem;  
  31. import android.view.View;  
  32. import android.view.Window;  
  33. import android.view.ContextMenu.ContextMenuInfo;  
  34. import android.widget.AdapterView;  
  35. import android.widget.LinearLayout;  
  36. import android.widget.ListView;  
  37. import android.widget.RelativeLayout;  
  38. import android.widget.TextView;  
  39. import android.widget.Toast;  
  40. import android.widget.AdapterView.OnItemClickListener;  
  41.   
  42. public class Record extends Activity implements RemoveListener {  
  43.   
  44.     private RecordSlideListView callLogListView;  
  45.     private AsyncQueryHandler asyncQuery;  
  46.     private RecordAdapter adapter;  
  47.     private List<RecordBean> callLogs;  
  48.     public static TextView tvTopBar;  
  49.   
  50.     private TextView tvRecordEmptyHint;  
  51.   
  52.     public static int inDel = 0;  
  53.   
  54.     @Override  
  55.     protected void onCreate(Bundle savedInstanceState) {  
  56.   
  57.         this.requestWindowFeature(Window.FEATURE_NO_TITLE);  
  58.         super.onCreate(savedInstanceState);  
  59.         setContentView(com.android.dialer.R.layout.zj_record);  
  60.   
  61.         tvTopBar = (TextView) findViewById(com.android.dialer.R.id.textViewRecord);  
  62.           
  63.         tvRecordEmptyHint = (TextView) findViewById(com.android.dialer.R.id.zj_record_empty_hint);  
  64.   
  65.         callLogListView = (RecordSlideListView) findViewById(com.android.dialer.R.id.call_log_list);  
  66.         callLogListView.setRemoveListener(this);  
  67.         asyncQuery = new MyAsyncQueryHandler(getContentResolver());  
  68.         init();  
  69.     }  
  70.   
  71.     private void init() {  
  72.         Uri uri = android.provider.CallLog.Calls.CONTENT_URI;  
  73.         String[] projection = { CallLog.Calls.DATE, CallLog.Calls.NUMBER,  
  74.                 CallLog.Calls.TYPE, CallLog.Calls.CACHED_NAME,  
  75.                 CallLog.Calls._ID, CallLog.Calls.DURATION, };  
  76.         asyncQuery.startQuery(0null, uri, projection,  
  77.                 "_id in (select max(_id)from calls group by number)"null,  
  78.                 CallLog.Calls.DEFAULT_SORT_ORDER);  
  79.     }  
  80.   
  81.     private class MyAsyncQueryHandler extends AsyncQueryHandler {  
  82.         public MyAsyncQueryHandler(ContentResolver cr) {  
  83.             super(cr);  
  84.         }  
  85.   
  86.         @Override  
  87.         protected void onQueryComplete(int token, Object cookie, Cursor cursor) {  
  88.             if (cursor != null && cursor.getCount() > 0) {  
  89.                 callLogs = new ArrayList<RecordBean>();  
  90.                 SimpleDateFormat sfd = new SimpleDateFormat("MM-dd HH:mm");  
  91.                 Date date;  
  92.                 cursor.moveToFirst();  
  93.                 for (int i = 0; i < cursor.getCount(); i++) {  
  94.                     cursor.moveToPosition(i);  
  95.                     date = new Date(cursor.getLong(cursor  
  96.                             .getColumnIndex(CallLog.Calls.DATE)));  
  97.                     String number = cursor.getString(cursor  
  98.                             .getColumnIndex(CallLog.Calls.NUMBER));  
  99.                     int type = cursor.getInt(cursor  
  100.                             .getColumnIndex(CallLog.Calls.TYPE));  
  101.                     String cachedName = cursor.getString(cursor  
  102.                             .getColumnIndex(CallLog.Calls.CACHED_NAME));  
  103.                     int id = cursor.getInt(cursor  
  104.                             .getColumnIndex(CallLog.Calls._ID));  
  105.                     // String duration = cursor.getString(cursor  
  106.                     // .getColumnIndex(CallLog.Calls.DURATION));  
  107.                     RecordBean callLogBean = new RecordBean();  
  108.                     callLogBean.setId(id);  
  109.                     callLogBean.setNumber(number);  
  110.                     callLogBean.setName(cachedName);  
  111.                     if (null == cachedName || "".equals(cachedName)) {  
  112.                         callLogBean.setName(number);  
  113.                     }  
  114.                     callLogBean.setType(type);  
  115.                     callLogBean.setDate(sfd.format(date));  
  116.                     // callLogBean.setDuration(duration);  
  117.                     callLogs.add(callLogBean);  
  118.                 }  
  119.                 if (callLogs.size() > 0) {  
  120.                     setAdapter(callLogs);  
  121.                     tvRecordEmptyHint.setVisibility(View.GONE);  
  122.   
  123.                 } else {  
  124.                     zjNoRecord();  
  125.   
  126.                 }  
  127.             }  
  128.             super.onQueryComplete(token, cookie, cursor);  
  129.         }  
  130.     }  
  131.   
  132.     private void setAdapter(List<RecordBean> callLogs) {  
  133.         adapter = new RecordAdapter(this, callLogs);  
  134.         callLogListView.setAdapter(adapter);  
  135.     }  
  136.   
  137.     private void zjNoRecord() {  
  138.         Toast.makeText(this, com.android.dialer.R.string.zjNoRecord,  
  139.                 Toast.LENGTH_LONG).show();  
  140.     }  
  141.   
  142.     public void zjToDialpad(View v) {  
  143.         // Intent intent = new Intent();  
  144.         // setResult(100, intent);  
  145.         Intent intent = new Intent(this,  
  146.                 com.android.dialer.DialtactsActivity.class);  
  147.         startActivity(intent);  
  148.     }  
  149.   
  150.     public void zjCloseRecord(View v) {  
  151.         // finish();  
  152.     }  
  153.   
  154.     public void zjToContact(View v) {  
  155.   
  156.         ComponentName componetName = new ComponentName(  
  157.                 "com.tchip.contacts.activities",  
  158.                 "com.tchip.contacts.activities.mPeopleActivity");  
  159.         try {  
  160.   
  161.             Intent intent = new Intent();  
  162.             intent.setComponent(componetName);  
  163.             startActivity(intent);  
  164.             //myStartActivity(intent);  
  165.             // finish();  
  166.         } catch (Exception err) {  
  167.             Log.e("ComponentName""" + err);  
  168.         }  
  169.   
  170.     }  
  171.   
  172.     public void myStartActivity(Intent intent)   
  173.     {  
  174.         final Intent toBeExecute = intent;  
  175.         final ProgressDialog progressDialog = ProgressDialog  
  176.                 .show(this,  
  177.                         this.getString(com.android.dialer.R.string.zj_progress_loading),  
  178.                         this.getString(com.android.dialer.R.string.zj_progress_wait));  
  179.         final Handler handler = new Handler();  
  180.         final Runnable callback = new Runnable() {  
  181.             public void run() {  
  182.                 progressDialog.dismiss();  
  183.             }  
  184.         };  
  185.         Thread thread = new Thread() {  
  186.             @Override  
  187.             public void run() {  
  188.                 // startActivityForResult(toBeExecute,0);  
  189.                 startActivity(toBeExecute);  
  190.                 handler.post(callback);  
  191.             }  
  192.         };  
  193.         thread.start();  
  194.     }  
  195.   
  196.     public void zjToMsg(View v) {  
  197.         ComponentName componetName = new ComponentName(  
  198.                 "com.android.mms",  
  199.                 "com.android.mms.ui.ConversationList");  
  200.         try {  
  201.             Intent intent = new Intent();  
  202.             intent.setComponent(componetName);  
  203.             startActivity(intent);  
  204.             //myStartActivity(intent);  
  205.             // finish();  
  206.         } catch (Exception e) {  
  207.       
  208.   
  209.         }  
  210.     }  
  211.   
  212.     @Override  
  213.     public boolean onKeyDown(int keyCode, KeyEvent event) {  
  214.         // TODO Auto-generated method stub  
  215.         if (KeyEvent.KEYCODE_BACK == keyCode) {  
  216.             Intent intent = new Intent(Intent.ACTION_MAIN);  
  217.             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
  218.             intent.addCategory(Intent.CATEGORY_HOME);  
  219.             startActivity(intent);  
  220.         }  
  221.         return super.onKeyDown(keyCode, event);  
  222.     }  
  223.   
  224.     @Override  
  225.     protected void onStart() {  
  226.         // TODO Auto-generated method stub  
  227.         super.onStart();  
  228.     }  
  229.   
  230.     @Override  
  231.     protected void onResume() {  
  232.         // TODO Auto-generated method stub  
  233.         super.onResume();  
  234.     }  
  235.   
  236.     @Override  
  237.     protected void onRestart() {  
  238.         // TODO Auto-generated method stub  
  239.         init();  
  240.         super.onRestart();  
  241.     }  
  242.   
  243.     // 滑动之后的回调方法  
  244.     @Override  
  245.     public void removeItem(RemoveDirection direction, int position) {  
  246.         String sildeNum = callLogs.get(position).getNumber(); // zj:获取滑动Item的号码  
  247.   
  248.         switch (direction) {  
  249.         case RIGHT:  
  250.   
  251.             tvTopBar.setBackgroundColor(Color.parseColor("#454545"));  
  252.             tvTopBar.setText(com.android.dialer.R.string.call_log_activity_title);  
  253.             RecordSlideListView.itemView.setBackgroundColor(Color  
  254.                     .parseColor("#ffffff"));  
  255.             Uri uri = Uri.parse("tel:" + sildeNum);  
  256.             Intent intent = new Intent(Intent.ACTION_CALL, uri);  
  257.             startActivity(intent);  
  258.             break;  
  259.         case LEFT:  
  260.             // Toast.makeText(this, "向左短信  "+ position,  
  261.             // Toast.LENGTH_SHORT).show();  
  262.             // rlRecordBack.setBackgroundColor(Color.parseColor("#1C86EE"));  
  263.             tvTopBar.setBackgroundColor(Color.parseColor("#454545"));  
  264.             tvTopBar.setText(com.android.dialer.R.string.call_log_activity_title);  
  265.             RecordSlideListView.itemView.setBackgroundColor(Color  
  266.                     .parseColor("#ffffff"));  
  267.             Uri uri2 = Uri.parse("smsto:" + sildeNum);  
  268.             Intent intent2 = new Intent(Intent.ACTION_SENDTO, uri2);  
  269.             startActivity(intent2);  
  270.             break;  
  271.   
  272.         default:  
  273.             break;  
  274.         }  
  275.     }  
  276.   
  277.   
  278.   
  279. }  


自定义组件SlideListView:

[java]  view plain copy
  1. package com.android.dialer.calllog;  
  2.   
  3. import android.content.Context;  
  4. import android.graphics.Color;  
  5. import android.util.AttributeSet;  
  6. import android.view.MotionEvent;  
  7. import android.view.VelocityTracker;  
  8. import android.view.View;  
  9. import android.view.ViewConfiguration;  
  10. import android.view.WindowManager;  
  11. import android.widget.AdapterView;  
  12. import android.widget.ListView;  
  13. import android.widget.Scroller;  
  14. import android.widget.TextView;  
  15.   
  16. public class RecordSlideListView extends ListView {  
  17.   
  18.   
  19.     private int slidePosition;  
  20.     private int downY;  
  21.     private int downX;  
  22.     private int screenWidth;  
  23.     public static View itemView;  
  24.     private Scroller scroller;  
  25.     private static final int SNAP_VELOCITY = 300;  
  26.     private VelocityTracker velocityTracker;  
  27.     private boolean isSlide = false;  
  28.     private int mTouchSlop;  
  29.     private RemoveListener mRemoveListener;  
  30.     private RemoveDirection removeDirection;  
  31.   
  32.     public enum RemoveDirection {  
  33.         RIGHT, LEFT;  
  34.     }  
  35.   
  36.     public RecordSlideListView(Context context) {  
  37.         this(context, null);  
  38.     }  
  39.   
  40.     public RecordSlideListView(Context context, AttributeSet attrs) {  
  41.         this(context, attrs, 0);  
  42.     }  
  43.   
  44.     public RecordSlideListView(Context context, AttributeSet attrs, int defStyle) {  
  45.         super(context, attrs, defStyle);  
  46.         screenWidth = ((WindowManager) context  
  47.                 .getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay()  
  48.                 .getWidth();  
  49.         scroller = new Scroller(context);  
  50.         mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();  
  51.     }  
  52.   
  53.     public void setRemoveListener(RemoveListener removeListener) {  
  54.         this.mRemoveListener = removeListener;  
  55.     }  
  56.   
  57.     @Override  
  58.     public boolean dispatchTouchEvent(MotionEvent event) {  
  59.         switch (event.getAction()) {  
  60.         case MotionEvent.ACTION_DOWN: {  
  61.             addVelocityTracker(event);  
  62.   
  63.             if (!scroller.isFinished()) {  
  64.                 return super.dispatchTouchEvent(event);  
  65.             }  
  66.             downX = (int) event.getX();  
  67.             downY = (int) event.getY();  
  68.   
  69.             slidePosition = pointToPosition(downX, downY);  
  70.   
  71.             if (slidePosition == AdapterView.INVALID_POSITION) {  
  72.                 return super.dispatchTouchEvent(event);  
  73.             }  
  74.   
  75.             itemView = getChildAt(slidePosition - getFirstVisiblePosition());  
  76.             break;  
  77.         }  
  78.         case MotionEvent.ACTION_MOVE: {  
  79.             if (Math.abs(getScrollVelocity()) > SNAP_VELOCITY  
  80.                     || (Math.abs(event.getX() - downX) > mTouchSlop && Math  
  81.                             .abs(event.getY() - downY) < mTouchSlop)) {  
  82.                 isSlide = true;  
  83.             }  
  84.             break;  
  85.         }  
  86.         case MotionEvent.ACTION_UP:  
  87.             recycleVelocityTracker();  
  88.             break;  
  89.         }  
  90.   
  91.         return super.dispatchTouchEvent(event);  
  92.     }  
  93.   
  94.     private void scrollRight() {  
  95.   
  96.         // tvTopBar =  
  97.         // (TextView)findViewById(com.android.dialer.R.id.textViewRecord);  
  98.         // tvTopBar.setBackgroundColor(Color.parseColor("#32CD32"));  
  99.         // tvTopBar.setText(com.android.dialer.R.string.zj_slide_right_to_call);  
  100.   
  101.         removeDirection = RemoveDirection.RIGHT;  
  102.         final int delta = (screenWidth + itemView.getScrollX());  
  103.         scroller.startScroll(itemView.getScrollX(), 0, -delta, 0,  
  104.                 Math.abs(delta));  
  105.         postInvalidate();  
  106.     }  
  107.   
  108.   
  109.     private void scrollLeft() {  
  110.   
  111.         removeDirection = RemoveDirection.LEFT;  
  112.         final int delta = (screenWidth - itemView.getScrollX());  
  113.         scroller.startScroll(itemView.getScrollX(), 0, delta, 0,  
  114.                 Math.abs(delta));  
  115.         postInvalidate();  
  116.     }  
  117.   
  118.     private void scrollByDistanceX() {  
  119.         if (itemView.getScrollX() >= screenWidth / 3) {  
  120.             scrollLeft();  
  121.         } else if (itemView.getScrollX() <= -screenWidth / 3) {  
  122.             scrollRight();  
  123.         } else {  
  124.             itemView.scrollTo(00);  
  125.             // zj:恢复Topbar的字和背景颜色  
  126.             itemView.setBackgroundColor(Color.parseColor("#ffffff"));  
  127.             Record.tvTopBar.setBackgroundColor(Color.parseColor("#454545"));  
  128.             Record.tvTopBar  
  129.                     .setText(com.android.dialer.R.string.call_log_activity_title);  
  130.         }  
  131.     }  
  132.   
  133.     @Override  
  134.     public boolean onTouchEvent(MotionEvent ev) {  
  135.         if (isSlide && slidePosition != AdapterView.INVALID_POSITION) {  
  136.   
  137.             addVelocityTracker(ev);  
  138.             final int action = ev.getAction();  
  139.             int x = (int) ev.getX();  
  140.             switch (action) {  
  141.             case MotionEvent.ACTION_MOVE:  
  142.                 int deltaX = downX - x;  
  143.                 downX = x;  
  144.   
  145.                 if (deltaX > 20) { // 向左sms #D1EEEE  
  146.                     itemView.setBackgroundColor(Color.parseColor("#D1EEEE"));  
  147.                     Record.tvTopBar.setBackgroundColor(Color  
  148.                             .parseColor("#1C86EE"));  
  149.                     Record.tvTopBar  
  150.                             .setText(com.android.dialer.R.string.zj_slide_left_to_sms);  
  151.   
  152.                 } else if (deltaX < -20) { // 向右call  
  153.                     itemView.setBackgroundColor(Color.parseColor("#D1EEEE"));  
  154.                     Record.tvTopBar.setBackgroundColor(Color  
  155.                             .parseColor("#32CD32"));  
  156.                     Record.tvTopBar  
  157.                             .setText(com.android.dialer.R.string.zj_slide_right_to_call);  
  158.   
  159.                 }  
  160.           
  161.                 itemView.scrollBy(deltaX, 0);  
  162.                 return true// 拖动的时候ListView不滚动  
  163.                 // break;  
  164.             case MotionEvent.ACTION_UP:  
  165.                 int velocityX = getScrollVelocity();  
  166.                 if (velocityX > SNAP_VELOCITY) {  
  167.                     scrollRight();  
  168.                 } else if (velocityX < -SNAP_VELOCITY) {  
  169.                     scrollLeft();  
  170.                 } else {  
  171.                     scrollByDistanceX();  
  172.                 }  
  173.   
  174.                 recycleVelocityTracker();  
  175.                 isSlide = false;  
  176.                 break;  
  177.             }  
  178.   
  179.             return true;  
  180.         }  
  181.         return super.onTouchEvent(ev);  
  182.     }  
  183.   
  184.     @Override  
  185.     public void computeScroll() {  
  186.         if (scroller.computeScrollOffset()) {  
  187.             itemView.scrollTo(scroller.getCurrX(), scroller.getCurrY());  
  188.   
  189.             postInvalidate();  
  190.   
  191.             if (scroller.isFinished()) {  
  192.                 if (mRemoveListener == null) {  
  193.                     throw new NullPointerException(  
  194.                             "RemoveListener is null, we should called setRemoveListener()");  
  195.                 }  
  196.   
  197.                 itemView.scrollTo(00);  
  198.                 mRemoveListener.removeItem(removeDirection, slidePosition);  
  199.             }  
  200.         }  
  201.     }  
  202.   
  203.   
  204.     private void addVelocityTracker(MotionEvent event) {  
  205.         if (velocityTracker == null) {  
  206.             velocityTracker = VelocityTracker.obtain();  
  207.         }  
  208.   
  209.         velocityTracker.addMovement(event);  
  210.     }  
  211.   
  212.   
  213.     private void recycleVelocityTracker() {  
  214.         if (velocityTracker != null) {  
  215.             velocityTracker.recycle();  
  216.             velocityTracker = null;  
  217.         }  
  218.     }  
  219.   
  220.   
  221.     private int getScrollVelocity() {  
  222.         velocityTracker.computeCurrentVelocity(1000);  
  223.         int velocity = (int) velocityTracker.getXVelocity();  
  224.         return velocity;  
  225.     }  
  226.   
  227.   
  228.     public interface RemoveListener {  
  229.         public void removeItem(RemoveDirection direction, int position);  
  230.     }  
  231.   
  232. }  


代码有点长,Adapter的就先不贴了。


转载请注明出处:周木水的优快云博客 http://blog.youkuaiyun.com/zhoumushui

我的GitHub:周木水的GitHub https://github.com/zhoumushui

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值