Android基础整合项目之节日群发助手

Android基础整合项目(一) 之节日群发助手part 1

——转载请注明出处:coder-pig



本节引言:


Android入门系列已经写了大半了,学习了这么多理论知识,不练下手怎么行呢?

在实际的开发中我们会遇到更多的问题,同时也能加固我们的基础知识!鉴于

笔者的水平有限,该项目,面对的是初学者,各位大牛路过不喜勿喷!好吧说下第一个

练手项目吧,前几天中秋节今天又是教师节,各种祝福短信满天飞,手打再群发,条条

短信一个样,没意思!直接用别人弄好的短信群发,别人又不知道你是谁,起码加个:

亲爱的XXX.我是隔壁老王,.....别人起码知道你是谁把!好了,废话不多说,开始app

的开发吧!




ps:目前app的基本功能已经实现,效果图如下,如果有需要可以进行下载

到后面会逐步完善相关功能:

效果图:



参考代码下载:

源码下载



正文:


本节要点图:







开发流程详解:


1)创建数据库

创建数据库文件:有两个表,分别为存储联系人的表contacts和存储节日祝福语的表festival

可以直接使用SQLite Expert或者其他SQLite的可视化工具创建表,可以代码创建或者手动创建

代码创建的话执行以下语句生成数据库表:

"CREATE TABLE festival(sentence_id INTEGER PRIMARY KEY AUTOINCREMENT,detail)"

"CREATE TABLE contacts(_id INTEGER PRIMARY KEY,pname,pnumber,pstate)"

接着对节日表进行数据的录入,结束的表结构如下:

festival表:


contacts表:





2)应用启动时判断数据库文件是否存在

在完成上述创建数据库的步骤后,接着我们需要把数据库文件复制到assert目录

这里我们要做什么呢?

在应用启动的时候我们需要判断data/data/<包名>/database下有没有我们的数据库文件

如果不存在的话,我们需要通过代码将数据库文件导入到指定目录下!


①先定义我们的数据库名称以及包名常量:

public static String dbName="my.db";//数据库的名字
private static String DATABASE_PATH="/data/data/com.jay.example.festivalsmshelper/databases/";

②接着定义判断是数据库文件是否存在的方法:

  1. public boolean checkDataBase(){  
  2.         SQLiteDatabase checkDB = null;  
  3.         try{  
  4.             String databaseFilename = DATABASE_PATH+dbName;  
  5.             checkDB =SQLiteDatabase.openDatabase(databaseFilename, null,  
  6.                     SQLiteDatabase.OPEN_READONLY);  
  7.         }catch(SQLiteException e){  
  8.                
  9.         }  
  10.         if(checkDB!=null){  
  11.             checkDB.close();  
  12.         }  
  13.         return checkDB !=null?true:false;  
  14.     }  
③假如数据库文件不存在的话我们需要将数据库文件复制到指定目录下:

  1. public void copyDataBase() throws IOException{  
  2.     String databaseFilenames =DATABASE_PATH+dbName;  
  3.     File dir = new File(DATABASE_PATH);  
  4.     if(!dir.exists())//判断文件夹是否存在,不存在就新建一个  
  5.         dir.mkdir();  
  6.     FileOutputStream os = null;  
  7.     try{  
  8.         os = new FileOutputStream(databaseFilenames);//得到数据库文件的写入流  
  9.     }catch(FileNotFoundException e){  
  10.         e.printStackTrace();  
  11.     }  
  12.     InputStream is = MainActivity.this.getAssets().open("my.db");  
  13.     byte[] buffer = new byte[4096];  
  14.     int count = 0;  
  15.     try{    
  16.         while((count=is.read(buffer))>0){  
  17.             os.write(buffer, 0, count);  
  18.             os.flush();  
  19.         }  
  20.     }catch(IOException e){e.printStackTrace();}  
  21.     is.close();  
  22.     os.close();    
  23. }  
    public void copyDataBase() throws IOException{
        String databaseFilenames =DATABASE_PATH+dbName;
        File dir = new File(DATABASE_PATH);
        if(!dir.exists())//判断文件夹是否存在,不存在就新建一个
            dir.mkdir();
        FileOutputStream os = null;
        try{
            os = new FileOutputStream(databaseFilenames);//得到数据库文件的写入流
        }catch(FileNotFoundException e){
            e.printStackTrace();
        }
        InputStream is = MainActivity.this.getAssets().open("my.db");
        byte[] buffer = new byte[4096];
        int count = 0;
        try{  
            while((count=is.read(buffer))>0){
                os.write(buffer, 0, count);
                os.flush();
            }
        }catch(IOException e){e.printStackTrace();}
        is.close();
        os.close();  
    }

④在MainActivity的onCreate()方法中加入下面的代码,用于调用上述两个方法:

  1. boolean dbExist = checkDataBase();  
  2. if(dbExist){}  
  3. else{//不存在就把assert里的数据库写入手机  
  4. try{  
  5.     copyDataBase();  
  6.     }catch(IOException e){throw new Error("复制数据库出错");}  
  7. }  
	boolean dbExist = checkDataBase();
	if(dbExist){}
	else{//不存在就把assert里的数据库写入手机
	try{
	    copyDataBase();
		}catch(IOException e){throw new Error("复制数据库出错");}
	}

⑤打开File Exploer查看数据库文件是否复制完毕:


如图就说明数据库文件复制完毕,是仅仅有一个my.db文件而已!后面那个my.do-journal是因为执行了

其他的操作生成的!




3)读取系统的联系人目录:

这里的话我们读取的仅仅是联系人的目录,并不包括sim卡中的联系人哦!

这块就涉及到了我们前面所学的使用系统提供的ContentProvider了!

我们先把系统提供的联系人的数据库文件找出来瞅瞅吧!

打开文件浏览器:data/data/com.android.providers.contacts/databases

下面的contacts2.db文件就是存储系统联系人的数据库文件了:


导出以后查看几个重要的基本表,以及相关字段:

contacts表


data表


phone_look_up表


raw_contact表


以上四个就是我们要留意的四个表了

好了,说下我们要获取的数据:联系人id,姓名,一个电话号码

于是我们定义一个类GetPhone类并定义一个读取联系人的方法,将读到数据存储到list集合中:

代码如下:

  1. public static List<Person> getPerson(Context context)  
  2.     {  
  3.         List<Person> persons = new ArrayList<Person>();  
  4.         ContentResolver cr = context.getContentResolver();  
  5.         Cursor cursor = cr.query(ContactsContract.Contacts.CONTENT_URI,nullnullnullnull);  
  6.         while(cursor.moveToNext()){  
  7.             Person person = new Person();  
  8.             //获取联系人id   
  9.             String contatId = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));  
  10.             person.setId(Integer.parseInt(contatId));  
  11.             //获取联系人姓名   
  12.             String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));  
  13.             person.setName(name);  
  14.             //因为一个联系人的电话号码可能有几个,但我们这里仅仅是获取一个就够了,所以就不循环遍历了  
  15.             Cursor phones = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,  
  16.                     null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID+"="+contatId, nullnull);  
  17.             phones.moveToFirst();  
  18.             String num = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));  
  19.             person.setNumber(num);  
  20.             person.setState(-1);  
  21.             persons.add(person);  
  22.             phones.close();  
  23.         }  
  24.         cursor.close();   
  25.         return persons;  
  26.     }  
public static List<Person> getPerson(Context context)
	{
		List<Person> persons = new ArrayList<Person>();
		ContentResolver cr = context.getContentResolver();
		Cursor cursor = cr.query(ContactsContract.Contacts.CONTENT_URI,null, null, null, null);
		while(cursor.moveToNext()){
			Person person = new Person();
			//获取联系人id
			String contatId = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
			person.setId(Integer.parseInt(contatId));
			//获取联系人姓名
			String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
			person.setName(name);
			//因为一个联系人的电话号码可能有几个,但我们这里仅仅是获取一个就够了,所以就不循环遍历了
			Cursor phones = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
					null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID+"="+contatId, null, null);
			phones.moveToFirst();
			String num = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
			person.setNumber(num);
			person.setState(-1);
			persons.add(person);
			phones.close();
		}
		cursor.close();	
		return persons;
	}



4)定义一个插入数据的方法:

该方法的参数是一个Person的对象,我们使用ContentValues来存储从person取出的不同数据!

再调用db.insert("contacts",null,contentValue)将记录插入到contacts表中

代码:

  1. public void insert(Person person){  
  2.     SQLiteDatabase db = dbOpenHelper.getWritableDatabase();  
  3.     ContentValues values = new ContentValues();  
  4.     values.put("_id", person.getId());  
  5.     values.put("pname", person.getName());  
  6.     values.put("pnumber", person.getNumber());  
  7.     values.put("pstate", person.getState());  
  8.     db.insert("contacts"null, values);  
  9. }  
	public void insert(Person person){
		SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
		ContentValues values = new ContentValues();
		values.put("_id", person.getId());
		values.put("pname", person.getName());
		values.put("pnumber", person.getNumber());
		values.put("pstate", person.getState());
		db.insert("contacts", null, values);
	}


5)在MainActivity中调用获取联系人的方法,插入数据

怕数据太多,出现卡住的情况,加个进度条,用户看着没那么不爽!

再new一个线程,一秒钟后让进度条消失,第一个界面消失,通过intent跳转

到第二个界面中!


代码:

  1. //加个进度条,数据太多可能卡住不好,用户看着不爽   
  2. final ProgressDialog dialog = ProgressDialog.show(this"提示""读取联系人中"falsetrue);  
  3. GetContactsService getContacts = new GetContactsService(getApplicationContext());  
  4. List<Person> persons = GetPerson.getPerson(MainActivity.this);  
  5. Cursor cursor = getContacts.query("select count(*) from contacts"null);  
  6. cursor.moveToFirst();  
  7. //判断联系人数目是否发生变更,没变更的话就不用调用下面的for循环了  
  8. if(persons.size() != cursor.getInt(0))  
  9. {  
  10.     for(Person p : persons)  
  11.     {  
  12.         System.out.println(p.toString());  
  13.         getContacts.insert(p);  
  14.     }  
  15. }  
  16.   
  17. //设置让圆形进度条过一秒后消失,以及第一个界面消失,跳转到第二个界面  
  18. new Thread()  
  19. {  
  20.     public void run()   
  21.     {  
  22.         try {  
  23.             sleep(1000);  
  24.         } catch (InterruptedException e) {e.printStackTrace();}  
  25.         dialog.dismiss();  
  26.         Intent it = new Intent(getApplicationContext(), ChooseActivity.class);  
  27.         startActivity(it);  
  28.         finish();  
  29.     };  
  30. }.start();  
		//加个进度条,数据太多可能卡住不好,用户看着不爽
		final ProgressDialog dialog = ProgressDialog.show(this, "提示", "读取联系人中", false, true);
		GetContactsService getContacts = new GetContactsService(getApplicationContext());
		List<Person> persons = GetPerson.getPerson(MainActivity.this);
		Cursor cursor = getContacts.query("select count(*) from contacts", null);
		cursor.moveToFirst();
		//判断联系人数目是否发生变更,没变更的话就不用调用下面的for循环了
		if(persons.size() != cursor.getInt(0))
		{
			for(Person p : persons)
			{
				System.out.println(p.toString());
				getContacts.insert(p);
			}
		}
		
		//设置让圆形进度条过一秒后消失,以及第一个界面消失,跳转到第二个界面
		new Thread()
		{
			public void run() 
			{
				try {
					sleep(1000);
				} catch (InterruptedException e) {e.printStackTrace();}
				dialog.dismiss();
				Intent it = new Intent(getApplicationContext(), ChooseActivity.class);
				startActivity(it);
				finish();
			};
		}.start();


6)运行后的效果查看:


将my.db文件导出后可以看到contacts表的data已经有数据了,就完成了

另外记得还有个Person类别漏了,定义四个属性:id,name,number,state(记录是否已经发送)

这里就不给出了,另外要创建出第二个Activity完成Intent的跳转哦!不然可是会报错的!





知识点总结:

1.判断app中的数据库文件是否存在?不存在的话如何使用输入流将文件写入到相应目录下

2.如何获得assert里的文件,从而获得输入流对象;

3.哪里可以找到系统保存联系人的数据库;相关表以及字段的了解!

读取表中我们需要的联系人信息!

4.调用database的insert()方法插入ContentValues类型的数据

5.获得数据库表有多少条记录的方法:

Cursor cursor = getContacts.query("select count(*) from contacts", null);

cursor.moveToFirst();

cursor.getInt(0);

6.获取List集合中的数据元素:list.size();

好了这一节就暂且到这里吧,如果对本文有什么建议,批评的,欢迎指出!

不慎感激!大笑项目的代码会随着后面深入慢慢完善的!

 

Android基础整合项目(一) 之节日群发助手part 2

——转载请注明出处:coder-pig


本节引言:

在上一节中我们已经做出了我们群发助手的第一个界面以及完成了联系人的读取以及数据库的

录入了,在这一节中将要完成的工作是:

1)自定义我们的ListView的列表项,两个TextView + CheckBox;

2)使用SimpleCursorAdapter将数据显示到ListView上;

3)实现listview的全选与全不选

4)点击重置按钮后,将数据库中的state都重置为-1

好了,那么开始本节的内容吧!


正文:

1.创建第二个Activity的布局:


布局代码如下:

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:id="@+id/RelativeLayout1"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent"  
  6.     android:orientation="vertical"  
  7.     tools:context="com.jay.example.festivalsmshelper.MainActivity" >  
  8.   
  9.     <TextView  
  10.         android:id="@+id/TextView1"  
  11.         android:layout_width="wrap_content"  
  12.         android:layout_height="wrap_content"  
  13.         android:layout_alignParentLeft="true"  
  14.         android:layout_alignParentTop="true"  
  15.         android:text="选择要发送的人" />  
  16.   
  17.     <ListView  
  18.         android:id="@+id/listcontacts"  
  19.         android:layout_width="match_parent"  
  20.         android:layout_height="match_parent"  
  21.         android:layout_alignParentLeft="true"  
  22.         android:layout_below="@+id/TextView1"  
  23.         android:layout_above="@+id/buttomlayout"  
  24.         android:layout_marginTop="10dp" >  
  25.     </ListView>  
  26.   
  27.     <LinearLayout  
  28.         android:id="@+id/buttomlayout"  
  29.         android:layout_width="match_parent"  
  30.         android:layout_height="wrap_content"  
  31.         android:layout_alignParentBottom="true"  
  32.         android:layout_alignParentLeft="true"  
  33.         android:orientation="horizontal" >  
  34.   
  35.         <Button  
  36.             android:layout_width="wrap_content"  
  37.             android:layout_height="wrap_content"  
  38.             android:id="@+id/btnall"  
  39.             android:text="全选" />  
  40.   
  41.         <Button  
  42.             android:layout_width="wrap_content"  
  43.             android:layout_height="wrap_content"  
  44.             android:id="@+id/btnreset"  
  45.             android:text="重置" />  
  46.   
  47.         <Button  
  48.             android:id="@+id/btnnext"  
  49.             android:layout_width="wrap_content"  
  50.             android:layout_height="wrap_content"  
  51.             android:text="下一步" />  
  52.         <TextView  
  53.             android:layout_marginLeft="20dp"  
  54.             android:layout_width="wrap_content"  
  55.             android:layout_height="wrap_content"  
  56.             android:text="已选"  
  57.             android:textSize="15sp" />  
  58.         <TextView  
  59.             android:layout_marginLeft="20dp"  
  60.             android:layout_width="wrap_content"  
  61.             android:layout_height="wrap_content"  
  62.             android:id="@+id/textshow"  
  63.             android:textSize="15sp" />  
  64.     </LinearLayout>  
  65.       
  66. </RelativeLayout>  
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/RelativeLayout1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.jay.example.festivalsmshelper.MainActivity" >

    <TextView
        android:id="@+id/TextView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:text="选择要发送的人" />

    <ListView
        android:id="@+id/listcontacts"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/TextView1"
        android:layout_above="@+id/buttomlayout"
        android:layout_marginTop="10dp" >
    </ListView>

    <LinearLayout
        android:id="@+id/buttomlayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:orientation="horizontal" >

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/btnall"
            android:text="全选" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/btnreset"
            android:text="重置" />

        <Button
            android:id="@+id/btnnext"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="下一步" />
        <TextView
            android:layout_marginLeft="20dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="已选"
            android:textSize="15sp" />
        <TextView
            android:layout_marginLeft="20dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/textshow"
            android:textSize="15sp" />
    </LinearLayout>
    
</RelativeLayout>



 

2.创建Listview的列表项布局文件:

其实就是三等分了布局,另外还隐藏了两个textview

代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:orientation="horizontal"  
  6.     android:gravity="center_horizontal"  
  7.      >  
  8.       
  9.     <TextView  
  10.         android:gravity="center"   
  11.         android:id="@+id/listid"  
  12.         android:layout_width="0dp"   
  13.         android:layout_weight="1"  
  14.         android:layout_height="match_parent"  
  15.         android:visibility="gone"  
  16.     />  
  17.       
  18.     <TextView  
  19.         android:gravity="center"   
  20.         android:id="@+id/listname"  
  21.         android:layout_width="0dp"   
  22.         android:layout_weight="1"  
  23.         android:layout_height="match_parent"  
  24.     />  
  25.   
  26.     <TextView  
  27.         android:gravity="center"   
  28.         android:id="@+id/listphone"  
  29.         android:layout_width="0dp"   
  30.         android:layout_weight="1"  
  31.         android:layout_height="match_parent"  
  32.     />  
  33.       
  34.     <TextView   
  35.         android:id="@+id/listhide"  
  36.         android:layout_width="wrap_content"   
  37.         android:layout_height="match_parent"  
  38.         android:visibility="gone"  
  39.           
  40.     />  
  41.       
  42.       
  43.     <CheckBox  
  44.         android:focusable="false"   
  45.         android:clickable="false"   
  46.         android:id="@+id/listchoice"  
  47.         android:layout_width="0dp"   
  48.         android:layout_weight="1"  
  49.         android:layout_height="match_parent"    
  50.     />  
  51.       
  52. </LinearLayout>  
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    android:gravity="center_horizontal"
     >
    
    <TextView
        android:gravity="center" 
    	android:id="@+id/listid"
    	android:layout_width="0dp" 
    	android:layout_weight="1"
    	android:layout_height="match_parent"
    	android:visibility="gone"
    />
    
    <TextView
        android:gravity="center" 
    	android:id="@+id/listname"
    	android:layout_width="0dp" 
    	android:layout_weight="1"
    	android:layout_height="match_parent"
    />

    <TextView
        android:gravity="center" 
    	android:id="@+id/listphone"
    	android:layout_width="0dp" 
    	android:layout_weight="1"
    	android:layout_height="match_parent"
    />
    
    <TextView 
    	android:id="@+id/listhide"
    	android:layout_width="wrap_content" 
    	android:layout_height="match_parent"
    	android:visibility="gone"
    	
    />
    
    
    <CheckBox
        android:focusable="false" 
        android:clickable="false" 
    	android:id="@+id/listchoice"
    	android:layout_width="0dp" 
    	android:layout_weight="1"
    	android:layout_height="match_parent"  
    />
    
</LinearLayout>



 


3.使用SimpleCursorAdapter绑定数据库与ListView:

代码如下:

  1. private void getContacts()   
  2.     {  
  3.         GetContactsService gs = new GetContactsService(ChooseActivity.this);  
  4.         Cursor cursor = gs.query("select _id,pname,pnumber,pstate from contacts"null);  
  5.         //注意:使用SimpleCursorAdapter的话,这个_id是必不可少的!不然一直报错  
  6.         //所以在前面需要弄一个隐藏的组件来放_id   
  7.         simpleCursorAdapter = new SimpleCursorAdapter(ChooseActivity.this, R.layout.listitem,   
  8.                 cursor, new String[]{"_id","pname","pnumber","pstate"},  
  9.                 new int[]{R.id.listid,R.id.listname,R.id.listphone,R.id.listhide});  
  10.         list.setAdapter(simpleCursorAdapter);  
  11.     }  
private void getContacts() 
	{
		GetContactsService gs = new GetContactsService(ChooseActivity.this);
		Cursor cursor = gs.query("select _id,pname,pnumber,pstate from contacts", null);
		//注意:使用SimpleCursorAdapter的话,这个_id是必不可少的!不然一直报错
		//所以在前面需要弄一个隐藏的组件来放_id
		simpleCursorAdapter = new SimpleCursorAdapter(ChooseActivity.this, R.layout.listitem, 
				cursor, new String[]{"_id","pname","pnumber","pstate"},
				new int[]{R.id.listid,R.id.listname,R.id.listphone,R.id.listhide});
		list.setAdapter(simpleCursorAdapter);
	}



 





注意!!!!使用SimpleCursorAdapter的话,绑定的数据库表中一定要有_id这个字段,或者as _id;

而且在绑定时取出的数据必须包含这个_id项,否则的话会报以下错误!

java.lang.IllegalArgumentException: column '_id' does not exist


4.在OnCreate( )中调用该方法:

在Activity中调用3的getContacts( ),就可以看到我们的已经把数据库的数据显示到ListView上了:




5.点击listview列表项执行的操作:

①为listView设置setOnItemClickListener方法,即点击事件

②点击后要左什么呢?改变checkbox的选中状态,以及统计有多少个选中,然后显示出来

先定义一个用于统计当前选中的复选框数量:

  1. private int setShow()  
  2. {  
  3.     int num = 0;  
  4.     for(int i = 0;i < list.getChildCount();i++)  
  5.     {  
  6.         LinearLayout layout = (LinearLayout) list.getChildAt(i);  
  7.         CheckBox cbx = (CheckBox) layout.findViewById(R.id.listchoice);  
  8.         if(cbx.isChecked())num++;  
  9.     }  
  10.     return num;  
  11. }  
	private int setShow()
	{
		int num = 0;
		for(int i = 0;i < list.getChildCount();i++)
		{
			LinearLayout layout = (LinearLayout) list.getChildAt(i);
			CheckBox cbx = (CheckBox) layout.findViewById(R.id.listchoice);
			if(cbx.isChecked())num++;
		}
		return num;
	}



 

接着实现点击后复选框选中,以及已选数目的改变:

  1. list.setOnItemClickListener(new OnItemClickListener() {  
  2.             @Override  
  3.             public void onItemClick(AdapterView<?> parent, View view,  
  4.                     int position, long id) {  
  5.                 CheckBox cb = (CheckBox) view.findViewById(R.id.listchoice);  
  6.                 TextView itemhide = (TextView) view.findViewById(R.id.listhide);  
  7.                 TextView itemid = (TextView) view.findViewById(R.id.listid);  
  8.                 int state = Integer.parseInt(itemhide.getText().toString());  
  9.                 //利用这个变量来区分按钮是否选中  
  10.                 state *= -1;  
  11.                 itemhide.setText(state+"");  
  12.                 if(state == 1)cb.setChecked(true);  
  13.                 else cb.setChecked(false);  
  14.                 textshow.setText(setShow()+"项");  
  15.             }  
  16.         });  
list.setOnItemClickListener(new OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				CheckBox cb = (CheckBox) view.findViewById(R.id.listchoice);
				TextView itemhide = (TextView) view.findViewById(R.id.listhide);
				TextView itemid = (TextView) view.findViewById(R.id.listid);
				int state = Integer.parseInt(itemhide.getText().toString());
				//利用这个变量来区分按钮是否选中
				state *= -1;
				itemhide.setText(state+"");
				if(state == 1)cb.setChecked(true);
				else cb.setChecked(false);
				textshow.setText(setShow()+"项");
			}
		});



 


完成上述代码后运行出现以下效果:




6.全选功能的实现:

就是选中全部的列表项,接着显示数目而已!

  1. btnall.setOnClickListener(new OnClickListener() {  
  2.       
  3.     @Override  
  4.     public void onClick(View v) {  
  5.         allflag *= -1;  
  6.         if(allflag == -1)  
  7.         {  
  8.             for(int i = 0;i < list.getChildCount();i++)  
  9.             {  
  10.                 LinearLayout layout = (LinearLayout) list.getChildAt(i);  
  11.                 CheckBox cbx = (CheckBox) layout.findViewById(R.id.listchoice);  
  12.                 cbx.setChecked(true);  
  13.                 btnall.setText("全不选");  
  14.                 textshow.setText(setShow()+"项");  
  15.             }  
  16.         }  
  17.         else if(allflag == 1)  
  18.         {  
  19.             for(int i = 0;i < list.getChildCount();i++)  
  20.             {  
  21.                 LinearLayout layout = (LinearLayout) list.getChildAt(i);  
  22.                 CheckBox cbx = (CheckBox) layout.findViewById(R.id.listchoice);  
  23.                 cbx.setChecked(false);  
  24.                 btnall.setText("全选");  
  25.                 textshow.setText(setShow()+"项");  
  26.             }  
  27.         }  
  28.           
  29.     }  
  30. });  
		btnall.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				allflag *= -1;
				if(allflag == -1)
				{
					for(int i = 0;i < list.getChildCount();i++)
					{
						LinearLayout layout = (LinearLayout) list.getChildAt(i);
						CheckBox cbx = (CheckBox) layout.findViewById(R.id.listchoice);
						cbx.setChecked(true);
						btnall.setText("全不选");
						textshow.setText(setShow()+"项");
					}
				}
				else if(allflag == 1)
				{
					for(int i = 0;i < list.getChildCount();i++)
					{
						LinearLayout layout = (LinearLayout) list.getChildAt(i);
						CheckBox cbx = (CheckBox) layout.findViewById(R.id.listchoice);
						cbx.setChecked(false);
						btnall.setText("全选");
						textshow.setText(setShow()+"项");
					}
				}
				
			}
		});


上述代码实现后的效果:





7.下一步按钮点击触发事件的重写:

在这个按钮事件要完成的工作:

①遍历listview,获取checkbox为选中状态的,将对应联系人id存储到集合中!

②将集合存放到Intent中,it.putIntegerArrayListExtra("ids", checkedId);


代码如下:

  1. btnnext.setOnClickListener(new OnClickListener() {  
  2.     public void onClick(View v) {  
  3.         ArrayList<Integer> checkedId = new ArrayList<Integer>();  
  4.         for(int i = 0;i < list.getChildCount();i++)  
  5.         {  
  6.             LinearLayout layout = (LinearLayout) list.getChildAt(i);  
  7.             CheckBox cbx = (CheckBox) layout.findViewById(R.id.listchoice);  
  8.             TextView txtid = (TextView) layout.findViewById(R.id.listid);  
  9.             if(cbx.isChecked())  
  10.                 checkedId.add(Integer.parseInt(txtid.getText().toString()));  
  11.         }  
  12.         //跳转到第三个页面,同时把数据存储到intent对象中   
  13.         Intent it = new Intent(ChooseActivity.this,ThridActivity.class);  
  14.         it.putIntegerArrayListExtra("ids", checkedId);  
  15.         startActivity(it);  
  16.     }  
  17. });  
		btnnext.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				ArrayList<Integer> checkedId = new ArrayList<Integer>();
				for(int i = 0;i < list.getChildCount();i++)
				{
					LinearLayout layout = (LinearLayout) list.getChildAt(i);
					CheckBox cbx = (CheckBox) layout.findViewById(R.id.listchoice);
					TextView txtid = (TextView) layout.findViewById(R.id.listid);
					if(cbx.isChecked())
						checkedId.add(Integer.parseInt(txtid.getText().toString()));
				}
				//跳转到第三个页面,同时把数据存储到intent对象中
				Intent it = new Intent(ChooseActivity.this,ThridActivity.class);
				it.putIntegerArrayListExtra("ids", checkedId);
				startActivity(it);
			}
		});

恩呢,第二个界面就做好了!本节也到此结束了!



知识点总结:

好了,最后总结下这节中用到的知识点:

1)在LinearLayout中使用weight属性将水平或者竖直方向平分成多份!

2)使用visibility对组件进行隐藏:visible(可见),invisible(不可见,但还占据空间),gone(不可见,也不占空间)

3)SimpleCursorAdapter绑定数据库与ListView需要注意必须要有:_id这个字段或者某字段 as _id;

4)如何统计listview复选框为选中状态的列数,遍历listview!

5)使用intent的putIntegerArrayListExtra存储ArrayList<T>集合类型的数据,传递到另一Activity中!





Android基础整合项目(一) 之节日群发助手part 3

——转载请注明出处:coder-pig


本节引言:

在前面两个章节中我们已经完成了群发助手的读联系人,存取数据库;使用

SimpleCursorAdapter绑定数据库与ListView;实现listview的全选与全不选;

也把需要拨打电话号码的id以list集合的形式通过Intent传送到了第三个界面

今天我们就来完成第三个界面的开发,工作如下:

 

1)完成第三个Activity的布局

2)解析第二个Activity通过Intent传送过来的List集合

3)读取数据表中的festival表中的节日祝福语,显示到界面上

4)完成切换祝福语的功能

5)完成发送统一祝福语的功能

6)完成发送不同祝福语的功能

7)使用具有列表和带确定按钮的AlertDialog

8)使用SmsManager完成短信的发送



正文:


1.完成第三个Activity的布局:


代码如下:

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:id="@+id/LinearLayout1"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent"  
  6.     android:orientation="vertical"  
  7.     tools:context="com.jay.example.festivalsmshelper.MainActivity" >  
  8.   
  9.     <LinearLayout   
  10.         android:layout_width="match_parent"  
  11.         android:layout_height="wrap_content"  
  12.         android:orientation="horizontal"  
  13.     >  
  14.         <EditText   
  15.             android:layout_width="wrap_content"  
  16.             android:layout_height="wrap_content"  
  17.             android:hint="亲爱的"  
  18.             android:id="@+id/editappellation"      
  19.         />  
  20.         <TextView   
  21.             android:layout_width="wrap_content"  
  22.             android:layout_height="wrap_content"  
  23.             android:text="XXX,我是"      
  24.         />  
  25.         <EditText   
  26.             android:layout_width="wrap_content"  
  27.             android:layout_height="wrap_content"  
  28.             android:hint="小猪"  
  29.             android:id="@+id/editme"      
  30.         />  
  31.     </LinearLayout>  
  32.     <TextView   
  33.         android:layout_width="match_parent"  
  34.         android:layout_height="150dp"      
  35.         android:id="@+id/textwish"  
  36.     />  
  37.     <LinearLayout   
  38.         android:layout_width="match_parent"  
  39.         android:layout_height="wrap_content"  
  40.         android:orientation="horizontal">  
  41.         <Button  
  42.             android:layout_width="wrap_content"  
  43.             android:layout_height="wrap_content"  
  44.             android:text="切换祝福语"  
  45.             android:id="@+id/btnchange"      
  46.         />  
  47.         <Button  
  48.             android:layout_width="wrap_content"  
  49.             android:layout_height="wrap_content"  
  50.             android:text="发送不同祝福语"  
  51.             android:id="@+id/btnsendunlike"      
  52.         />      
  53.     </LinearLayout>  
  54.     <LinearLayout   
  55.         android:layout_width="match_parent"  
  56.         android:layout_height="wrap_content"  
  57.         android:orientation="horizontal"  
  58.     >  
  59.         <Button  
  60.             android:layout_width="wrap_content"  
  61.             android:layout_height="wrap_content"  
  62.             android:text="发送统一祝福语"  
  63.             android:id="@+id/btnsendlike"      
  64.         />  
  65.         <Button  
  66.             android:layout_width="wrap_content"  
  67.             android:layout_height="wrap_content"  
  68.             android:text="返回"  
  69.             android:id="@+id/btnback"      
  70.         />     
  71.     </LinearLayout>  
  72. </LinearLayout>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/LinearLayout1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.jay.example.festivalsmshelper.MainActivity" >

    <LinearLayout 
  		android:layout_width="match_parent"
  		android:layout_height="wrap_content"
  		android:orientation="horizontal"
  	>
        <EditText 
        	android:layout_width="wrap_content"
        	android:layout_height="wrap_content"
        	android:hint="亲爱的"
        	android:id="@+id/editappellation"    
        />
        <TextView 
        	android:layout_width="wrap_content"
        	android:layout_height="wrap_content"
        	android:text="XXX,我是"    
        />
        <EditText 
        	android:layout_width="wrap_content"
        	android:layout_height="wrap_content"
        	android:hint="小猪"
        	android:id="@+id/editme"    
        />
    </LinearLayout>
    <TextView 
    	android:layout_width="match_parent"
    	android:layout_height="150dp"    
        android:id="@+id/textwish"
    />
    <LinearLayout 
  		android:layout_width="match_parent"
  		android:layout_height="wrap_content"
  		android:orientation="horizontal">
        <Button
        	android:layout_width="wrap_content"
        	android:layout_height="wrap_content"
        	android:text="切换祝福语"
        	android:id="@+id/btnchange"    
        />
        <Button
        	android:layout_width="wrap_content"
        	android:layout_height="wrap_content"
        	android:text="发送不同祝福语"
        	android:id="@+id/btnsendunlike"    
        />    
    </LinearLayout>
    <LinearLayout 
  		android:layout_width="match_parent"
  		android:layout_height="wrap_content"
  		android:orientation="horizontal"
  	>
        <Button
        	android:layout_width="wrap_content"
        	android:layout_height="wrap_content"
        	android:text="发送统一祝福语"
        	android:id="@+id/btnsendlike"    
        />
        <Button
        	android:layout_width="wrap_content"
        	android:layout_height="wrap_content"
        	android:text="返回"
        	android:id="@+id/btnback"    
        />   
    </LinearLayout>
</LinearLayout>



 

2.解析第二个Activity传送过来的List集合:

直接用list存储即可,增强for循环可以去掉,只是用来确认传过来的集合是否有数据,

从而避免空指针问题的出现

  1. Intent it = getIntent();  
  2.         final List<Integer> list = it.getIntegerArrayListExtra("ids");  
  3.         int i = 1;  
  4.         //下面这个语句用于查看是否有传值过来,传了什么值,通常用log.?来跟踪错误  
  5.         for(Integer j:list)  
  6.         {  
  7.             System.out.println(j);  
  8.         }  
Intent it = getIntent();
		final List<Integer> list = it.getIntegerArrayListExtra("ids");
		int i = 1;
		//下面这个语句用于查看是否有传值过来,传了什么值,通常用log.?来跟踪错误
		for(Integer j:list)
		{
			System.out.println(j);
		}



 

3.读取数据库表中的festival表的祝福语并显示

简单的一条查询语句即可:

  1. sql = "select detail from festival where sentence_id = " + num+"";  
  2.         textwish.setText(getWish(sql));  
sql = "select detail from festival where sentence_id = " + num+"";
		textwish.setText(getWish(sql));

另外因为我们的操作很多都要查询数据库,就直接写到一个方法中getWish()

  1. private String getWish(String sql)  
  2.     {  
  3.           
  4.         String wish = null;  
  5.         GetContactsService gcs = new GetContactsService(ThridActivity.this);  
  6.         Cursor curosr =gcs.query(sql, null);  
  7.         curosr.moveToFirst();  
  8.         wish = curosr.getString(0);  
  9.         curosr.close();  
  10.         return wish;  
  11.     }  
private String getWish(String sql)
	{
		
		String wish = null;
		GetContactsService gcs = new GetContactsService(ThridActivity.this);
		Cursor curosr =gcs.query(sql, null);
		curosr.moveToFirst();
		wish = curosr.getString(0);
		curosr.close();
		return wish;
	}


 

ps:num是一个整数1-10都可以,看数据库表的记录数决定的



4.完成祝福语的切换功能:

其实这里就是简单的改变上面那个num的数字而已,自增++

然后等于十的时候把num重置为1即可

  1. btnchange.setOnClickListener(new OnClickListener() {  
  2.     public void onClick(View v) {  
  3.             if(num == 10)num = 1;  
  4.             else ++num;  
  5.             sql = "select detail from festival where sentence_id = " + num+"";  
  6.             textwish.setText(getWish(sql));  
  7.     }  
  8. });  
		btnchange.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
					if(num == 10)num = 1;
					else ++num;
					sql = "select detail from festival where sentence_id = " + num+"";
					textwish.setText(getWish(sql));
			}
		});



 

5)完成发送统一祝福语的功能:

就是给每个联系人发送相同的短信,这里的话,先读取传过来的集合获取id

然后根据id读取Contacts表中id对应的联系人名称,电话号码;

将这两个组合到一起,作为对话框的列表项的资源数组

还需要创建一个带列表的对话框,添加一个确定按钮


代码:

  1. btnsendlike.setOnClickListener(new OnClickListener() {  
  2.             @Override  
  3.             public void onClick(View v) {  
  4.                 String ids = "";  
  5.                 //将list集合中的数据取出来,结果可能是:7,8,9,这样的  
  6.                 for(Integer j:list)  
  7.                 {  
  8.                     ids = ids + j + ",";  
  9.                 }  
  10.                 //去掉小尾巴","直接去掉最后一位即可   
  11.                 ids = ids.substring(0, ids.length()-1);  
  12.                 System.out.println(ids);  
  13.                 //需要取出Contacts表中的联系人信息,用于等下对话框内容显示以及发送短信时  
  14.                 sql = "select * from contacts where _id in("+ids+")";  
  15.                 System.out.println(sql);  
  16.                 List<Map<String,String>> lc = getContacts(sql);  
  17.                 //取出集合中的元素放到字符串数据中  
  18.                 //判断需创建的数组的长度   
  19.                 int length = 0;  
  20.                 for(Map<String,String> mp:lc)  
  21.                 {  
  22.                     length++;  
  23.                 }  
  24.                 final int lg = length;  
  25.                 String name[] = new String[length];  
  26.                 String number[] = new String[length];  
  27.                 String show[] = new String[length];  
  28.                 final String finumber[] = number;  
  29.                 final String finame[] = name;  
  30.                 int i = 0;  
  31.                 for(Map<String,String> mp:lc)  
  32.                 {  
  33.                     name[i] = mp.get("name");  
  34.                     number[i] = mp.get("number");  
  35.                     Log.i("name[i]", name[i]);  
  36.                       
  37.                     Log.i("number[i]", number[i]);  
  38.                     i++;  
  39.                 }     
  40.                 for(int o = 0;o < i;o++)  
  41.                 {  
  42.                     show[o] = name[o] + ":" + number[o];  
  43.                     Log.i("show[o]", show[o]);  
  44.                 }  
  45.                 //弹出确认的对话框:   
  46.                 builder = new AlertDialog.Builder(ThridActivity.this);  
  47.                 builder.setTitle("统一发送祝福语模式\n请确认发送人员:");  
  48.                 builder.setIcon(R.drawable.ic_launcher);  
  49.                 builder.setItems(show,null);  
  50.                 builder.setPositiveButton("确定发送"new DialogInterface.OnClickListener()  
  51.                 {  
  52.                     @Override  
  53.                     public void onClick(DialogInterface dialog, int which) {  
  54.                         for(int p = 0;p < lg;p++)  
  55.                         {  
  56.                             String num = finumber[p];  
  57.                             String wish = editappellation.getText().toString() +  finame[p] + "我是"+  
  58.                                     editme.getText().toString() +  
  59.                                     textwish.getText().toString();  
  60.                             sendMessage(num, wish);  
  61.                         }  
  62.                           
  63.                     }  
  64.                       
  65.                 });  
  66.                 alert = builder.create();    
  67.                 alert.show();    
  68.             }  
  69.         });  
btnsendlike.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				String ids = "";
				//将list集合中的数据取出来,结果可能是:7,8,9,这样的
				for(Integer j:list)
				{
					ids = ids + j + ",";
				}
				//去掉小尾巴","直接去掉最后一位即可
				ids = ids.substring(0, ids.length()-1);
				System.out.println(ids);
				//需要取出Contacts表中的联系人信息,用于等下对话框内容显示以及发送短信时
				sql = "select * from contacts where _id in("+ids+")";
				System.out.println(sql);
				List<Map<String,String>> lc = getContacts(sql);
				//取出集合中的元素放到字符串数据中
				//判断需创建的数组的长度
				int length = 0;
				for(Map<String,String> mp:lc)
				{
					length++;
				}
				final int lg = length;
				String name[] = new String[length];
				String number[] = new String[length];
				String show[] = new String[length];
				final String finumber[] = number;
				final String finame[] = name;
				int i = 0;
				for(Map<String,String> mp:lc)
				{
					name[i] = mp.get("name");
					number[i] = mp.get("number");
					Log.i("name[i]", name[i]);
					
					Log.i("number[i]", number[i]);
					i++;
				}	
				for(int o = 0;o < i;o++)
				{
					show[o] = name[o] + ":" + number[o];
					Log.i("show[o]", show[o]);
				}
				//弹出确认的对话框:
				builder = new AlertDialog.Builder(ThridActivity.this);
				builder.setTitle("统一发送祝福语模式\n请确认发送人员:");
				builder.setIcon(R.drawable.ic_launcher);
				builder.setItems(show,null);
				builder.setPositiveButton("确定发送", new DialogInterface.OnClickListener()
				{
					@Override
					public void onClick(DialogInterface dialog, int which) {
						for(int p = 0;p < lg;p++)
						{
							String num = finumber[p];
							String wish = editappellation.getText().toString() +  finame[p] + "我是"+
									editme.getText().toString() +
									textwish.getText().toString();
							sendMessage(num, wish);
						}
						
					}
					
				});
                alert = builder.create();  
                alert.show();  
			}
		});



 

6)创建发送短信的方法:

  1. private void sendMessage(String number,String message)  
  2. {  
  3.     SmsManager.getDefault().sendTextMessage(number,    
  4.                null, message, nullnull);  
  5.     Toast.makeText(getApplicationContext(), "发送成功", Toast.LENGTH_SHORT).show();  
  6.     //在模拟器环境中我们需要查看发出的短信内容或者真机调试时不想浪费短信钱   
  7.     //就使用log.i查看发出的短信内容即可   
  8.     Log.i("sendMessage", number+message);  
  9. }  
	private void sendMessage(String number,String message)
	{
		SmsManager.getDefault().sendTextMessage(number,  
                null, message, null, null);
		Toast.makeText(getApplicationContext(), "发送成功", Toast.LENGTH_SHORT).show();
		//在模拟器环境中我们需要查看发出的短信内容或者真机调试时不想浪费短信钱
		//就使用log.i查看发出的短信内容即可
		Log.i("sendMessage", number+message);
	}


另外,使用上述方法发送短信,你的手机是不会保留发送的信息记录的;

其实就是后台发送短信!



7)好了,完成5,6步后程序基本成型了:

完成前面6步后,程序已经可以完成基本功能了:

效果如下:


接着查看我们的Logcat,可以看到发送的信息内容:




8)最后再完成随机祝福语部分


就是随机给不同的人发送不同内容的短信:

其实这里和上面那个部分是大同小异的,仅仅是修改发送短信时

的祝福语内容,使用随机数决定发送的祝福语而已,仅仅需要添加下述代码即可:

1)取出Festival表中的detail字段的所有记录,存储到字符串数组中

  1. //将所有的祝福语短信存储到字符串数组中   
  2.                 String sql = "select detail from festival";  
  3.                 String[] showmsg = new String[10];  
  4.                 GetContactsService gcService = new GetContactsService(ThridActivity.this);  
  5.                 Cursor curosr =gcService.query(sql, null);  
  6.                 int s = 0;  
  7.                 curosr.moveToFirst();  
  8.                 while(curosr.moveToNext())  
  9.                 {  
  10.                     showmsg[s] = curosr.getString(0);  
  11.                     s++;  
  12.                 }                 
  13.                 final String[] fishowmsg = showmsg;   
//将所有的祝福语短信存储到字符串数组中
				String sql = "select detail from festival";
				String[] showmsg = new String[10];
				GetContactsService gcService = new GetContactsService(ThridActivity.this);
				Cursor curosr =gcService.query(sql, null);
				int s = 0;
				curosr.moveToFirst();
				while(curosr.moveToNext())
				{
					showmsg[s] = curosr.getString(0);
					s++;
				}				
				final String[] fishowmsg = showmsg; 


2)使用随机数,随机的取出祝福语

  1. for(int p = 0;p < lg;p++)  
  2.                         {  
  3.                             String num = finumber[p];  
  4.                             //只需要修改输出时的祝福语内容即可  
  5.                             int randow =  (int) (Math.random() * 10);  
  6.                             String wish = editappellation.getText().toString() +  finame[p] + "我是"+  
  7.                                     editme.getText().toString() +  
  8.                                     fishowmsg[randow];  
  9.                             sendMessage(num, wish);  
  10.                         }  
for(int p = 0;p < lg;p++)
						{
							String num = finumber[p];
							//只需要修改输出时的祝福语内容即可
							int randow =  (int) (Math.random() * 10);
							String wish = editappellation.getText().toString() +  finame[p] + "我是"+
									editme.getText().toString() +
									fishowmsg[randow];
							sendMessage(num, wish);
						}

运行截图:



截至之当前的项目代码:

代码下载


知识点总结:

1)解析Intent中存储的list集合:

Intent it = getIntent();
final List<Integer> list = it.getIntegerArrayListExtra("ids");

2)SQLite数据库的相关操作,以及cursor的使用

切忌一点,使用cursor一定要调用cursor.moveToFirst()不然会报错哦!

另外可以调用moveToNext()可以使游标后移!可使用getXxx获取不同类型的数据

3)使用SmsManager.getDefault().sendTextMessage(number, null, message, null, null);

发送短信

4)生成1~10内的随机整数:int randow =  (int) (Math.random() * 10); 





好了,节日群发助手这个项目开发就到这里了,app仅仅是具有雏形,UI和代码都没有

优化,存在一定的冗余和bug,各位学员在自己编写的过程中可以自行的修改或者优化

本文的初衷是帮助各位初学者巩固相关的知识点的!后续会推出类似的知识点整合

项目,敬请关注,谢谢大笑

当然有时间也会修改下这个app,正如大牛所说的,改下UI或许会是个好的app




 



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值