Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 智能短信 2

Android 智能短信 2

編輯:關於Android編程

刪除選中的短信:

我們刪除短信其實很簡單,但是我們要實現一個對話框去顯示,還需要一個對話框的進度條。

刪除短信操作就是操作數據庫就行了。使用內容解析者去操作,但是我們要去看看到底要刪除的uri是什麼。

我們發現我們要刪除一個就需要刪除一個聯系人,所以我們直接刪除這個人名下的所有短信就可以了。我們可以找到他的id去刪除。

 

對短信內容進行操作需要寫短信的權限。

 

Uri URI_SMS=Uri.parse("content://sms");

我們先找到uri然後我們在Fragment裡面定義一個方法。

 

 

		case R.id.bt_conversation_delete:
			selectedConversationId = adapter.getSelectedConversationId();
    if(selectedConversationId.size()==0)
    {
    	return;
    }
    deleteAll();

我們先獲取到集合。

 

然後如果不為空就調用刪除方法。

 

	public void deleteAll() {
		for (Integer integer : selectedConversationId) {
			String where = "thread_id=" + integer;
			getActivity().getContentResolver().delete(ConstantValues.URI_SMS,
					where, null);
		}

	}
——————————————————————————————
確定取消對話框:

 

我們要自定義樣式,所以我們要先創建一個包dialog包。然後創建一個類。但是由於一個項目對話框有很多。所以我們也要創建一個基類。

 

 

public abstract class BaseDialog extends AlertDialog implements android.view.View.OnClickListener{

	public BaseDialog(Context context) {
		super(context);
	}

@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	initView();
	initListener();
	initData();
}
public abstract void initView();
public abstract  void initListener();
public abstract void initData();
public abstract void processClick(View v);
@Override
public void onClick(View v) {
	processClick(v);
	
};

}

注意這裡我們要用到的點擊事件是,View 類的,不是Dialog類的。原因是因為我們使用,的對話框是自定義的,所以對話框上面的按鈕也是自己定義的Button.

 

所以我們要使用View類的。

 

然後我們需要創建一個對話框的布局文件。

 



    

    

    

    


我們為了實現讓這個自定義的dialog布局變成圓角的,所以我們創建drawable,選擇shape(形狀) 。

 

 


    
    


先定義android:shape為長方形

 

corners就是角:定義角的弧度為8dp

solid實體:顏色為xxxx:

 

這樣寫的話每一個對話框都需要家這樣一行代碼。

如果我們需要每一個對話框都是這種樣式。

我們就需要在style中去定義一個主題。

 


我們這樣就設置了一個自定義的style 使用的形狀就是我們自己定義的。

 

 

我們的BaseDialog繼承了AlertDialog他有一個構造方法:

 

	public BaseDialog(Context context) {
		
		super(context, R.style.BaseDialog);
	
	}


 

這裡注意由於AlertDialog有三個構造函數,其中有一個是可以接受 Theme參數的。

所以我們直接super(context,Theme)把Theme傳遞給他即可。

 

 

 

然後我們在我們自己定義的dialog文件中,把android:background=""刪除。因為你在baseDialog中已經定義了,如果你在布局文件中在定義就會被覆蓋。

 

然後我們在我們定義個Dialog類中 設置布局文件給他。

 

	@Override
	public void initView() {
		setContentView(R.layout.dialog_comfirm);

	}

然後我們在Fragment文件中給他設置方法去調用

 

 

	public void showDelete() {
		ComfirmDialog dialog = new ComfirmDialog(getActivity());
		dialog.show();
	}

——————————————————————

 

確定取消對話框按鈕背景:

我們先給Button設置背景,同樣我們給他設置選擇器

android:background="@drawable/selector_bt_bg"

然後我們發現設置完之後左下角和右下角都不是圓角了。

所以我們要自己定義一個selector.

 

我們要單獨給他設置兩個shape.

 


    
    



    
    




    
    

 

 

然後給button設置 android:background="@drawable/bg_rigth_bt_sec"即可。
————————————————————————————————

給確定取消對話框設置屬性和按鈕偵聽:

 

我們通過構造函數傳遞 Title 和Message 進來。

 

public class ComfirmDialog extends BaseDialog {

	private String title;

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

	private String message;
	private TextView tv_dialog_title;
	private TextView tv_dialog_msg;

	public ComfirmDialog(Context context, String title, String message) {
		super(context);
		this.title = title;
		this.message = message;
	}

	@Override
	public void initView() {
		setContentView(R.layout.dialog_comfirm);
		tv_dialog_title = (TextView) findViewById(R.id.tv_dialog_title);
		tv_dialog_msg = (TextView) findViewById(R.id.tv_dialog_msg);

	}

	@Override
	public void initListener() {

	}

	@Override
	public void initData() {
		tv_dialog_title.setText(getTitle());
		tv_dialog_msg.setText(getMessage());
	}

	@Override
	public void processClick(View v) {

	}

}

然後我們在new 的時候把參數傳遞進來就行了。

 

ComfirmDialog dialog = new ComfirmDialog(getActivity(),"提示","請確定是否要刪除");

 

設置好了之後我們要去設置 按鈕的監聽事件

 

為了讓我們這個對話框還能實現其他的點擊功能我們不能寫死 點擊按鈕的事件。

所以我們在定義一個用於監聽的接口。

 

public interface DialogBtListener {

	void onCancle();

	void onConfrim();

}

 

 

然後我們在自定義的Dialog類裡面的構造函數接收這個接口;

 

private String message;
	private TextView tv_dialog_title;
	private TextView tv_dialog_msg;
	private Button bt_dialog_cancel;
	private Button bt_dialog_confirm;
	private DialogBtListener onConfrimListener;

	public void setOnConfrimListener(DialogBtListener onConfrimListener) {
		this.onConfrimListener = onConfrimListener;
	}

	public ComfirmDialog(Context context, String title, String message,
			DialogBtListener onConfrimListener) {
		super(context);
		this.title = title;
		this.message = message;
		this.onConfrimListener = onConfrimListener;
	}

	@Override
	public void initView() {
		setContentView(R.layout.dialog_comfirm);
		tv_dialog_title = (TextView) findViewById(R.id.tv_dialog_title);
		tv_dialog_msg = (TextView) findViewById(R.id.tv_dialog_msg);
		bt_dialog_cancel = (Button) findViewById(R.id.bt_dialog_cancel);
		bt_dialog_confirm = (Button) findViewById(R.id.bt_dialog_confirm);
	}

	@Override
	public void initListener() {
		bt_dialog_cancel.setOnClickListener(this);
		bt_dialog_confirm.setOnClickListener(this);
	}

	@Override
	public void initData() {
		tv_dialog_title.setText(getTitle());
		tv_dialog_msg.setText(getMessage());
	}

	@Override
	public void processClick(View v) {
		switch (v.getId()) {
		case R.id.bt_dialog_cancel:
			if (onConfrimListener != null) {
				onConfrimListener.onCancle();
			 }
			break;
		case R.id.bt_dialog_confirm:
			if (onConfrimListener != null) {
				onConfrimListener.onConfrim();
			}
			break;

		}
		dismiss();
	}


不管我們點確定還是取消 我們都要關閉對話框 所以最後都要調用dismiss();

 

 

然後我們可以判斷傳遞進來的這個監聽接口是不是空,不是空我們在調用。

如果我們要實現其他的監聽接口 我們可以調用set接口的方法去改變接口,然後去判斷就可以了。


然後我們在Fragment裡面去實現方法就可以了。

 

	public void showDelete() {
		ComfirmDialog dialog = new ComfirmDialog(getActivity(),"提示","請確定是否要刪除",new DialogBtListener() {
			
			@Override
			public void onConfrim() {
				deleteAll();
			}
			
			@Override
			public void onCancle() {
			}
		});
		dialog.show();
	}

_______________________________________

 

刪除的優化:

 

我們還要注意一點:就是我們操作數據庫其實就是一個耗時操作。如果數據很多,我們很可能ANR所以我們需要在子線程進行。

 

還有一個注意要點就是,如果我們刪除了集合裡面選擇的短信,那麼這些id就沒用了。

所以我們必須清空集合,不然我們下次再去查的時候就會出問題。

 

	public void deleteAll() {
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				
				for (Integer integer : selectedConversationId) {
					String where = "thread_id=" + integer;
					getActivity().getContentResolver().delete(ConstantValues.URI_SMS,
							where, null);
				}
				selectedConversationId.clear();
				
			}
		}).start();

	}
handler.sendEmptyMessage(WHAT_DELETE_COMPLETE);
當我們刪除完畢後應該退出。選擇模式,然後菜單應該也變成編輯模式菜單。

 

所以我們需要更新UI,但是不能在子線程更新。所以我們在主線程new Handler來處理消息。

 

 

	private static final int WHAT_DELETE_COMPLETE = 0;
	private Handler handler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			switch (msg.what) {
			case WHAT_DELETE_COMPLETE:
				adapter.setIsSelectMode(false);
				showEditMenu();
				break;

			}
		}
	};

我們定義一個全局的常量。

 

然後判斷如果是這個常量就進行退出選擇模式。顯示編輯菜單。

 

——————————————————————————

刪除進度條對話框:

我們的刪除對話框其實和確定取消對話框是一樣的,所以我們只需要把中間的文本。

換成對話框。按鈕去掉一個,然後改成取消中斷。

 

我們把中間的textView改成:

 

    

但是我們現在的需求是需要 顯示進度的時候 顏色是紅色。

 

所以我們需要去改變進度條的顏色,所以我們現在先去styles.xml文件中定義一個style.

 


然後我們直接去看看progerssBar的源碼 看看谷歌怎麼定義的。

 

 



然後我們發現有這樣一行:

 

item name="android:progressDrawable">@android:drawable/progress_horizontal

這一行就是和顏色相關的。

所以我們點進去看看源碼怎麼定義的。

 


    
    
        
            
            
        
    
    
    
        
            
                
                
            
        
    
    
    
        
            
                
                
            
        
    
    

我們發現他定義了這樣一個層, 用來設置 進度條的屬性。

 

所以我們自己也定義一個這樣的層,然後修改屬性,我們就可以直接使用它了。

老方法直接去drawable創建一個layer-list

注意,裡面有一個gradient屬性,這個屬性就是用來控制漸變的。進度條的顏色其實就是漸變的。

 

我們直接去progress裡面把gradient屬性刪除。創建一個

一個固定的顏色。

 



    
        
            
            
            
        
    
    
        
            
                
                
                
            
        
    


然後我們就可以創建一個自定義的Dialog了 和創建取消確定對話框步驟一樣。

 

 

public class DeleteDialog extends BaseDialog {

	private ProgressBar pb_delete_;
	private TextView tv_delete_title;
	private Button bt_dialog_interrupe;
	private DeleteDialogListener onDeleteLintener;
	//最大值
	private int Maxprogerss;

	public DeleteDialog(Context context,int Maxprogerss,DeleteDialogListener onLintener) {
		super(context);
		this.onDeleteLintener=onLintener;
		this.Maxprogerss=Maxprogerss;
	}

	@Override
	public void initView() {
		setContentView(R.layout.dialog_delete);
		pb_delete_ = (ProgressBar) findViewById(R.id.pb_delete_);
		tv_delete_title = (TextView) findViewById(R.id.tv_delete_title);
		bt_dialog_interrupe = (Button) findViewById(R.id.bt_dialog_interrupe);
	}

	@Override
	public void initListener() {
		bt_dialog_interrupe.setOnClickListener(this);
	}

	@Override
	public void initData() {
		//初始化
		tv_delete_title.setText("正在刪除(0/"+Maxprogerss+")");
		pb_delete_.setMax(Maxprogerss);
	}

	@Override
	public void processClick(View v) {
		switch (v.getId()) {
		case R.id.bt_dialog_interrupe:
			if (onDeleteLintener!=null) {
				onDeleteLintener.interrupe();
			}
			dismiss();
			break;

		}
	}
	//暴露一個方法給外界調用這個方法設置進度條
	public void serProgress(int progerss){
		tv_delete_title.setText("正在刪除("+progerss+"/"+Maxprogerss+")");
		pb_delete_.setProgress(progerss);
	}

}

接下來我們就去Fragment裡面調用。

 

 

boolean isStop = false;
	public void deleteAll() {
		dialog = new DeleteDialog(getActivity(), selectedConversationId.size(),
				new DeleteDialogListener() {

					@Override
					public void interrupe() {
                        isStop=true;
					}
				});
dialog.show();
		new Thread(new Runnable() {

			@Override
			public void run() {
				for (Integer integer : selectedConversationId) {
					SystemClock.sleep(1000);
					if(isStop){
						isStop=false;
						break;
					}
					String where = "thread_id=" + integer;
					getActivity().getContentResolver().delete(
							ConstantValues.URI_SMS, where, null);
					Message msg = Message.obtain();
					msg.what = WHAT_UPDATA_PROGRESS;
					msg.arg1 = count++;
					handler.sendMessage(msg);
				}
				selectedConversationId.clear();
				handler.sendEmptyMessage(WHAT_DELETE_COMPLETE);
			}
		}).start();
		
	}

因為我們是在刪除的時候顯示對話框。所以我們直接在刪除的方法裡面 調用對話框的構造函數。

 

然後我們創建一個 標簽。。當我們點擊中斷的時候我們就把他設置為true。

然後在線程中,檢查標簽是否為true,如果是就直接跳出循環。

 

因為我們要修改 UI 。然後要獲取當前的進度條的當前進程。所以我們要攜帶數據去傳遞消息,那麼就不能使用空消息。

 

 

	private static final int WHAT_UPDATA_PROGRESS = 1;
	private int count = 1;
	private Handler handler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			switch (msg.what) {
			case WHAT_DELETE_COMPLETE:
				adapter.setIsSelectMode(false);
				adapter.notifyDataSetChanged();
				showEditMenu();
				dialog.dismiss();
				break;
			case WHAT_UPDATA_PROGRESS:
				
				dialog.serProgress(msg.arg1);
				break;
			}
		}
	};
當我們完成的時候要關閉對話框。

 

————————————————————————

創建會話詳細Acticity並傳遞數據:

 

我們需要傳遞adress和會話id過去。因為每一個條目的數據都不一樣,所以我們應該在

會話詳情的activity在對這些數據在

	public void onItemClick(AdapterView parent, View view,
					int position, long id) {
				// 因為我們使用CursorAdapter這個類,他幫我們封裝好了getItem可以返回你指定的Cursor對象
				if (adapter.getIsSelectMode()) {
					adapter.SelectSingle(position);
				} else {
					//會話詳情
					Intent intent = new Intent(getActivity(),
							ConversationDataActivity.class);
					Cursor cursor = (Cursor) adapter.getItem(position);
					ConversationBean creatConversationCursor = ConversationBean
							.creatConversationCursor(cursor);

					intent.putExtra("address",
							creatConversationCursor.getAddress());
					intent.putExtra("thead_id",
							creatConversationCursor.getThread_id());
					startActivity(intent);
				}

			}
		});

	}

public class ConversationDataActivity extends BaseActivity {

	@Override
	public void initView() {

	}

	@Override
	public void initListener() {

	}

	@Override
	public void initData() {
		Intent intent = getIntent();
		if (intent != null) {
			String address = intent.getStringExtra("address");
			int thread_id = intent.getIntExtra("thread_id", -1);
			LogUtils.i("yss", address+thread_id);
		}
	}

	@Override
	public void progressClick(View v) {

	}

}


——————————————————————

定義ConversationDetailActivity的布局文件:

 

我們先要定義一個標題欄,因為標題欄我們可以復用,所以我們直接專門創建一個標題欄的XML.

 



    

    



 

然後我們在創建activity的xml.

我們可以直接使用

調用我們定義好的布局 標題欄文件。顯示到當前的布局文件中。

 



    

    
    

    

        

 

 

這裡注意一下我們要設置一個 minHeiget:這個屬性就是你的最低寬度是這麼多。

如果你的控件沒有其他東西的時候他就會變成minHeiget.


當我們的控件高要是其他控件占用完後,剩下的全部都是它的。那麼我們就需要設置權重。

 

——————————————————————————

初始化標題欄:

 

public class ConversationDataActivity extends BaseActivity {

	private String address;
	private int thread_id;

	@Override
	public void initView() {
		setContentView(R.layout.activity_desconversation);
	}

	@Override
	public void initListener() {

	}

	@Override
	public void initData() {
		Intent intent = getIntent();
		if (intent != null) {
			address = intent.getStringExtra("address");
			thread_id = intent.getIntExtra("thread_id", -1);
			initTitlebar();
		}
	}

	public void initTitlebar() {
		findViewById(R.id.iv_titlebar_back).setOnClickListener(this);
		String name = ContactDao.getNameforAddress(getContentResolver(),
				address);
		((TextView) findViewById(R.id.tv_titlebar_title)).setText(TextUtils
				.isEmpty(name) ? address : name);

	}

	@Override
	public void progressClick(View v) {
		switch (v.getId()) {
		case R.id.iv_titlebar_back:
			finish();
			break;

		}
	}

}

當我們intent不為空的時候調用。初始化titlebar.

 

————————————————————————

異步查詢會話所屬的短信:

 

		String [] projection ={
			"_id",
			"body",
			"type",
			"date"
		};
		String selection="thread_id ="+thread_id;
		System.out.println(selection);
		Cursor cursor = getContentResolver().query(ConstantValues.URI_SMS, projection, selection, null, null);
		CursorUtils.printCursor(cursor);
	}

我們先查詢看看能不能查詢出數據。

 

我們現在需要sms表裡面的這些數據,第一個是應為CursorAdapter必須要查的_id

剩下三個是我們在界面需要使用的數據。

但是我們這裡使用的是同步的查詢,所以下面我們用異步查詢來實現。

 

 

		String [] projection ={
			"_id",
			"body",
			"type",
			"date"
		};
		String selection="thread_id ="+thread_id;
//		System.out.println(selection);
		SimpleQueryHandler queryHandler = new SimpleQueryHandler(getContentResolver());
		queryHandler.startQuery(0, null, ConstantValues.URI_SMS, projection, selection, null, "date");
		

記得這裡我們需要用升序。所以我們直接使用默認排序 即可。升序就是最新的在下面。

 

 

定義布局文件:

 


    
    
    

同樣當你字很少的時候,我們需要定義一個minWidth。

 

————————————————————————————

ConversationDetailActivity的listView內容顯示:

我們先創建了Adapter,這裡和昨天步驟都是一樣的。

 

public class DesConversaionAdapter extends CursorAdapter {

	public DesConversaionAdapter(Context context, Cursor c) {
		super(context, c);

	}

	@Override
	public View newView(Context context, Cursor cursor, ViewGroup parent) {

		return View.inflate(context, R.layout.desconversation, null);
	}

	@Override
	public void bindView(View view, Context context, Cursor cursor) {
          ViewHolder holder = getHolder(view);
          
	}
	public ViewHolder getHolder(View view){
		ViewHolder holder = (ViewHolder) view.getTag();
		if (holder==null) {
			 holder = new ViewHolder(view);
			 view.setTag(holder);
		}
		return holder;
		
	}

	class ViewHolder {
		private TextView tv_desconversation_date;
		private TextView tv_desconversation_receive;
		private TextView tv_desconversation_send;

		public ViewHolder(View view) {
			tv_desconversation_date = (TextView) view
					.findViewById(R.id.tv_desconversation_date);
			tv_desconversation_receive = (TextView) view
					.findViewById(R.id.tv_desconversation_receive);
			tv_desconversation_send = (TextView) view
					.findViewById(R.id.tv_desconversation_send);

		}
	}
}

然後我們在去activity裡面給ListView設置adapter

 

 

	adapter = new DesConversaionAdapter(this, null);
		//顯示會話的所有短信。
		lv_desconversation.setAdapter(adapter);
		
		String[] projection = { "_id", "body", "type", "date" };
		
		String selection = "thread_id =" + thread_id;
		// System.out.println(selection);
		SimpleQueryHandler queryHandler = new SimpleQueryHandler(
				getContentResolver());
		//這裡cookie傳遞adapter過去
		queryHandler.startQuery(0, adapter, ConstantValues.URI_SMS, projection,
				selection, null, "date");

接著我們就去adapter裡面先把Cursor封裝到bean裡面。因為這樣可以比較方便的操作數據。

 

 

步驟和上一天的差不多 :

 

	public void bindView(View view, Context context, Cursor cursor) {
		ViewHolder holder = getHolder(view);

		// 這裡的cursor不需要移動,因為源代碼已經幫我們做了
		SmsBean smsBean = SmsBean.createFormCursor(cursor);
		if (DateUtils.isToday(smsBean.getDate())) {
			holder.tv_desconversation_date.setText(DateFormat.getTimeFormat(
					context).format(smsBean.getDate()));

		} else {
			holder.tv_desconversation_date.setText(DateFormat.getDateFormat(
					context).format(smsBean.getDate()));
		}
		holder.tv_desconversation_receive
				.setVisibility(smsBean.getType() == ConstantValues.TYPE_RECEIVE ? View.VISIBLE
						: View.GONE);
		holder.tv_desconversation_send
				.setVisibility(smsBean.getType() == ConstantValues.TYPE_SEND ? View.VISIBLE
						: View.GONE);
		if (smsBean.getType() == ConstantValues.TYPE_RECEIVE) {
			holder.tv_desconversation_receive.setText(smsBean.getBody());
		}
		holder.tv_desconversation_send.setText(smsBean.getBody());
	}

但是這樣我們做完 發現條目不是很好看,條目還能點擊,且還有線。所以我們需要去去掉它。

 

 

 

    
    

但是還有一個問題就是每一條都會顯示時間,這樣非常不好看。我們可以設置相隔多少時間就不會去顯示時間了。比如你第一條和第二條都是在同一個時間發的,我們就顯示一個時間。這個間隔可以自己按需求定義。

 

 

我們先把判斷時間顯示的函數,封裝成一個類,因為我們要多次調用。

 

	private void showDate(Context context, ViewHolder holder, SmsBean smsBean) {
		if (DateUtils.isToday(smsBean.getDate())) {
			holder.tv_desconversation_date.setText(DateFormat.getTimeFormat(
					context).format(smsBean.getDate()));

		} else {
			holder.tv_desconversation_date.setText(DateFormat.getDateFormat(
					context).format(smsBean.getDate()));
		}
	}

然後我們定義一個常量

 

private static final int DURTION = 3 * 60 * 1000;

用來設置我們的時間間隔。

 

		// 先判斷是否是第一條,第一條沒有上一條應該直接顯示
		if (cursor.getPosition() == 0) {
			showDate(context, holder, smsBean);
		} else {
			// 判斷上一條短信和當前的短信是否相差三分鐘
			long date = getPreviousDate(cursor.getPosition());
			if (smsBean.getDate() - date > DURTION) {
				holder.tv_desconversation_date.setVisibility(View.VISIBLE);
				showDate(context, holder, smsBean);

			} else {
				holder.tv_desconversation_date.setVisibility(View.GONE);
			}
		}

	private long getPreviousDate(int postion) {
		//獲取上一條的內容所以需要減1
		Cursor cursor = (Cursor) getItem(postion-1);
		SmsBean smsBean = SmsBean.createFormCursor(cursor);

		return smsBean.getDate();
	}

 

一些小問題:

當我們進入會話詳情頁面的時候。發現短信不能移動到最下面要手動移。

解決:lv_desconversation.setSelection(cursor.getCount());

我們使用這個方法就可以讓ListView初始的位置在你指定的位置。我們使用cursor條目,是因為最大值就是他的條目數。

但是有一個問題就是,我們到底應該在哪調用這個方法,因為如果你的cursor沒有值。那麼這個方法就是無效的。

 

public class SimpleQueryHandler extends AsyncQueryHandler {

	public SimpleQueryHandler(ContentResolver cr) {
		super(cr);
	
	}
	//這個方法就是用來接收剛剛傳遞的兩個參數,當查詢完成的時候調用
     @Override
    protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
   
    	super.onQueryComplete(token, cookie, cursor);
//    	CursorUtils.printCursor(cursor);
    	if(cookie!=null&& cookie instanceof CursorAdapter){
			//查詢得到的cursor,交給CursorAdapter,由它把cursor的內容顯示至listView
			((CursorAdapter)cookie).changeCursor(cursor);
    	}
    }
}

最終查詢到Cursor且將值傳遞回去,我們是使用了changCursor方法。

 

所以我們應該去重寫這個方法,然後在這個方法裡面加上。

 

所以我們就去繼承CursorAdatper的類裡面重寫這個方法。

第一步就是要將lv傳遞給Adapter.在構造函數的時候傳遞進來。

 

 

	@Override
	public void changeCursor(Cursor cursor) {
		super.changeCursor(cursor);
		lv.setSelection(cursor.getCount());

	}

ChangeCursor是第一次查詢的時候會調用,但是當你短信內容改變的時候不會調用。

 

所以:

問題2:

當我們點擊編輯框的時候想輸入字符。發現輸入法框擋住了我們的界面顯示的內容。

解決:

我們可以給listview設置

lv_desconversation.setTranscriptMode(AbsListView.TRANSCRIPT_MODE_ALWAYS_SCROLL

這個屬性可以讓listview在內容改變的時候 移動到最新的位置。

 

還有一個方法就是通過設置Activity的屬性來設置軟鍵盤和activity之間的一些屬性。

在清單文件中:

 

        
            
            
        

我們可以使用 windowSoftInputMode這個屬性來給 activity和軟件盤設置 效果。

 

通常我們都是一個state和一個adjust 組合在一起使用。

 

各值的含義:

stateUnspecified:軟鍵盤的狀態並沒有指定,系統將選擇一個合適的狀態或依賴於主題的設置

stateUnchanged:當這個activity出現時,軟鍵盤將一直保持在上一個activity裡的狀態,無論是隱藏還是顯示

stateHidden:用戶選擇activity時,軟鍵盤總是被隱藏

stateAlwaysHidden:當該Activity主窗口獲取焦點時,軟鍵盤也總是被隱藏的

stateVisible:軟鍵盤通常是可見的

stateAlwaysVisible:用戶選擇activity時,軟鍵盤總是顯示的狀態

adjustUnspecified:默認設置,通常由系統自行決定是隱藏還是顯示

adjustResize:該Activity總是調整屏幕的大小以便留出軟鍵盤的空間

adjustPan:當前窗口的內容將自動移動以便當前焦點從不被鍵盤覆蓋和用戶能總是看到輸入內容的部分


 

————————————————————

 

發送短信:

我們創建一個SmsDao類,然後創建一個方法用來發送短信。

然後我們去實現發送短信的邏輯:

 

public class SmsDao {
	public static void sendSms(String address,String body){
		SmsManager manager = SmsManager.getDefault();
		ArrayList smss = manager.divideMessage(body);
		for (String text : smss) {
			manager.sendTextMessage(address, null, text, sentIntent, null);
		}
		
	}

}

這裡我們要使用一個pendingIntent 去實現 廣播

 

 

對於pendingIntent的理解可以看下面這個博客。

 

http://blog.csdn.net/yuzhiboyi/article/details/8484771

 

public class SmsDao {
	public static void sendSms(String address,String body,Context context){
		SmsManager manager = SmsManager.getDefault();
		Intent intent = new Intent("com.chen.text.sendsms");
		ArrayList smss = manager.divideMessage(body);
		PendingIntent sentIntent=PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_ONE_SHOT
);
		for (String text : smss) {
			manager.sendTextMessage(address, null, text, sentIntent, null);
		}
		
	}

}


 

這裡我們需要注意,我們PendingIntent裡面的intent參數 是一個廣播接收者,接收的action屬性。

 

最後的Flage有如下參數:

 

FLAG_ONE_SHOT:利用 FLAG_ONE_SHOT獲取的PendingIntent只能使用一次,即使再次利用上面三個方法重新獲取,再使用PendingIntent也將失敗。

FLAG_NO_CREATE:利用FLAG_NO_CREAT獲取的PendingIntent,若描述的Intent不存在則返回NULL值.

FLAG_CANCEL_CURRENT:如果描述的PendingIntent已經存在,則在產生新的Intent之前會先取消掉當前的。你可用使用它去檢索新的Intent,如果你只是想改變Intent中的額外數據的話。通過取消先前的Intent,可用確保只有最新的實體可用啟動它。如果這一保證不是問題,考慮flag_update_current。

FLAG_UPDATE_CURRENT:最經常使用的是FLAG_UPDATE_CURRENT,因為描述的Intent有 更新的時候需要用到這個flag去更新你的描述,否則組件在下次事件發生或時間到達的時候extras永遠是第一次Intent的extras。

使用 FLAG_CANCEL_CURRENT也能做到更新extras,只不過是先把前面的extras清除,另外FLAG_CANCEL_CURRENT和 FLAG_UPDATE_CURRENT的區別在於能否新new一個Intent,FLAG_UPDATE_CURRENT能夠新new一個 Intent,而FLAG_CANCEL_CURRENT則不能,只能使用第一次的Intent。

 

創建一個廣播接受者去接收這些消息。

 

public class SmsSendReceiver extends BroadcastReceiver {

	@Override
	public void onReceive(Context context, Intent intent) {
		int code = getResultCode();
		if (code == Activity.RESULT_OK) {
			ToastUtils.showToast(context, "發送成功");
		} else {
			ToastUtils.showToast(context, "發送失敗");

		}

	}

}

清單文件中定義如下:

 

 

 
            
                
            
        

但是由於我們這樣發送短信沒有進入到數據庫,所以沒有顯示到我們的界面上面。

 

 

 

public class SmsDao {
	public static void sendSms(String address, String body, Context context) {
		SmsManager manager = SmsManager.getDefault();
		Intent intent = new Intent("com.chen.text.sendsms");
		ArrayList smss = manager.divideMessage(body);
		PendingIntent sentIntent = PendingIntent.getBroadcast(context, 0,
				intent, PendingIntent.FLAG_ONE_SHOT);
		for (String text : smss) {
			// 這個API 只能發送,但是不進入數據庫
			manager.sendTextMessage(address, null, text, sentIntent, null);
			addSms(context, text, address);
		}

	}

	public static void addSms(Context context, String text, String address) {
		ContentValues values = new ContentValues();
		values.put("address", address);
		values.put("body", text);
		values.put("type", ConstantValues.TYPE_SEND);
		context.getContentResolver().insert(ConstantValues.URI_SMS, values);

	}
}

我們需要添加這三個數據進去,這樣才能在我們的頁面輸出。

 

——————————————————————————

新建短信界面:

先創建Activity的布局文件,我們先設置一個框架然後在改。

 



    

    

        

        

        
    

    

然後在 Fragment裡面設置點擊事件。

 

 

		case R.id.bt_conversation_newSms:
			Intent intent = new Intent(getActivity(),NewsActivity.class);
			startActivity(intent);
			break;

_______________________________________

 

輸入框自動搜索號碼:

我們在activity先設置的標題欄。和上一天的一樣。

 

我們為了實現自動搜索:AutoCompleteTextView。使用這個控件,我們就可以實現。

其實這個控件繼承於EditView.所以其實他就是一個 編輯框。

 

這個組件顯示內容也是使用Adapter來給他設置顯示的內容。

因為我們顯示的數據也是來自於數據庫,所以我們還是使用CursorAdapter。

 

我們需要給他設置一個條目的布局。

 


    
    
    


 

實現顯示搜索框的下拉欄的數據顯示:

 

		AutoSreachAdapter adapter = new AutoSreachAdapter(this, null);
		//給我們的輸入框設置Adapter,用於輸入框的下拉欄顯示數據。
		actv_newmsg_address.setAdapter(adapter);
		
		adapter.setFilterQueryProvider(new FilterQueryProvider() {
			
			//這是一個回調函數。就是當我們在這個輸入框裡面輸入數據的時候就會調用這個方法。用於查詢數據
			@Override
			public Cursor runQuery(CharSequence constraint) {
				
				return null;
			}
		});
		

這個方法就是當我們在輸入框字符的時候就會查詢。

 

你輸入了之後刪除一個字符他也會調用這個方法。

 

如果你設置組件是2個字符開始查詢。那麼就會顯示你輸入的字符。

但是你輸入一個字符的時候顯示的是null.

當我們在xml裡面添加這個屬性的時候就會一個字符的時候查詢;

android:completionThreshold="1"

 

所以我們在輸入框中輸入的是號碼,也就是我們要進行模糊查詢的條件。

 

我們可以使用官方的提供的 URI

Phone.CONTENT_URI

 

這個URI可以查詢出很多數據。我們需要的是

display_name:姓名

data1:號碼

contact_id:聯系人的id

 

我們直接進行模糊查詢,只要包含我們輸入的數字就輸出。

 

	public void initData() {
		AutoSreachAdapter adapter = new AutoSreachAdapter(this, null);
		// 給我們的輸入框設置Adapter,用於輸入框的下拉欄顯示數據。
		actv_newmsg_address.setAdapter(adapter);

		adapter.setFilterQueryProvider(new FilterQueryProvider() {

			// 這是一個回調函數。就是當我們在這個輸入框裡面輸入數據的時候就會調用這個方法。用於查詢數據
			// constraint當前輸入框的文本。

			@Override
			public Cursor runQuery(CharSequence constraint) {
				String[] projection = { "data1", "display_name", "_id" };
				String selection="data1 like '%"+constraint+"%'";
				Cursor cursor = getContentResolver().query(Phone.CONTENT_URI,
						projection, selection, null, null);

				
				return cursor;
			}
		});

		initTitleBar();

	}

返回的Cursor對象,就是直接把這個cursor交給adapter

 

然後我們就可以去adapter 裡面去設置參數。

 

public class AutoSreachAdapter extends CursorAdapter {

	public AutoSreachAdapter(Context context, Cursor c) {
		super(context, c);

	}

	@Override
	public View newView(Context context, Cursor cursor, ViewGroup parent) {

		return View.inflate(context, R.layout.item_autotv_sreach, null);
	}

	@Override
	public void bindView(View view, Context context, Cursor cursor) {
		ViewHolder holder = getHolder(view);
		holder.tv_autosreach_name.setText(cursor.getString(cursor.getColumnIndex("display_name")));
		holder.tv_autosreach_address.setText(cursor.getString(cursor.getColumnIndex("data1")));
	}

	public ViewHolder getHolder(View view) {
		ViewHolder holder = (ViewHolder) view.getTag();
		if (holder == null) {
			holder = new ViewHolder(view);
			view.setTag(holder);
		}
		return holder;
	}

	class ViewHolder {
		private TextView tv_autosreach_name;
		private TextView tv_autosreach_address;

		public ViewHolder(View view) {
			tv_autosreach_name = (TextView) view
					.findViewById(R.id.tv_autosreach_name);
			tv_autosreach_address = (TextView) view
					.findViewById(R.id.tv_autosreach_address);
		}

	}

}

還有一個問題就是我們點擊 條目的時候應該把他的號碼顯示到我們的輸入框上。

 

這個條目你點擊的時候它默認調用的是。

 

	@Override
	public CharSequence convertToString(Cursor cursor) {
		// TODO Auto-generated method stub
		return super.convertToString(cursor);
	}

這個方法,他會默認的吧cursor.tostring()輸出到輸入框上。我們直接修改它要他輸出號碼。

 

 

我們還可以去設置下拉欄的背景圖片,間隔之類的。

\

偏移就是你的下拉欄離你的輸入框的位移量。

_____________________________________________

通過系統提供的Activity選擇聯系人:

我們先查看源碼的聯系人的activity

找到\ContactsListActivity

然後我們找到過濾器。

\

PICK (選擇)

類型我們隨便選擇一個寫即可。

 

		case R.id.bt_newmsg_send:
			Intent intent = new Intent(Intent.ACTION_PICK);
			intent.setType("vnd.android.cuosor.dir/phone");
			startActivityForResult(intent, 0);
			break;

 

 

我們需要在點擊聯系人的時候應該返回他的號碼,到輸入框。所以我們要調用

onActivityResult 方法。

 

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		//data中會攜帶一個URi 就是你選擇的聯系人的uri
		Uri uri = data.getData();
		if (uri!=null) {
			CursorUtils.printCursor(getContentResolver().query(uri, null, null, null, null));
			
		}
		super.onActivityResult(requestCode, resultCode, data);
		
	}
我們發現通過data返回的是一個uri通過這個uri我們可以查看返回的數據有哪些。

 

查看後發現,有display_name:聯系人名字

has_phone_number:聯系人是否有電話。

_id:其實就是聯系人的contact_id.

 

但是沒有我們需要的號碼,所以我們只能通過上面的查詢,再去查詢一次,這一次通過_id去查詢即可。

如果沒有號碼就不會查了
 

 

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		// data中會攜帶一個URi 就是你選擇的聯系人的uri
		Uri uri = data.getData();
		if (uri != null) {
			// 查詢聯系人id和是否有號碼
			String[] projection = { "_id", "has_phone_number" };
			// 不需要查詢條件,因為這個 uri是指定你點擊的聯系人的uri 只有一條。不需要條件。
			Cursor cursor = getContentResolver().query(uri, projection, null,
					null, null);
			cursor.moveToFirst();
			String _id = cursor.getString(0);
			int has_phone_number = cursor.getInt(1);
			if (has_phone_number == 0) {
				ToastUtils.showToast(getApplicationContext(), "沒有號碼");
			} else {
				Cursor query = getContentResolver().query(Phone.CONTENT_URI,
						new String[] { "data1" }, "contact_id=" + _id, null,
						null);
				query.moveToFirst();
				String address = query.getString(0);
				actv_newmsg_address.setText(address);
			}
		}
		super.onActivityResult(requestCode, resultCode, data);

	}

這樣查詢就完畢了。但是我們有一個問題就是為什麼cursor沒有判斷是否為空,這是因為,我們是通過選擇一個聯系人去做查詢的,既然我們都能選擇得到,那麼就肯定存在。

 

還有一個問題就是我們在選擇完聯系人,號碼會現在輸入框,但是有時候會彈出來自動查詢的列表,這說明我們的焦點還在輸入框裡面。所以我們應該把焦點放在輸入內容的組件裡面。

 

只需要在下面加上et_newmsg_body.requestFocus();即可

——————————————————

發送短信:

直接調用上面我們寫好的發送方法即可。

 

		case R.id.bt_newmsg_send:
			String body = et_newmsg_body.getText().toString().trim();
			String address = actv_newmsg_address.getText().toString().trim();
			if (!TextUtils.isEmpty(address)&&!TextUtils.isEmpty(body)) {
				SmsDao.sendSms(address, body, this);
			}
			break;

但是有一個小問題,就是如果我們用軟鍵盤輸入的時候,軟鍵盤的框會擋住發送的按鈕。

 

 

所以我們需要讓他可以滾動。

 

 
            

 

 

包裹其他的內容即可。這裡我們設置一個LinearLayout就是因為我們要裡面的布局為垂直的。

 


 

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved