安卓开发学习记录(续)
文章目录
- 十一、综合训练(购物车功能)
- 十二、内容提供者Provider
十一、综合训练(购物车功能)
实现功能:
手机商品页面展示,加入购物车功能,商品详情页面,清空购物车,删除购物车商品。
- 数据库准备,准备的数据库有商品数据库,还有存储购物车信息的数据库,使用SqliteOpenHelper进行操作,商品数据库的字段有id,description,price,picPath,购物车字段id,goodsId,count,实现如下:
package com.example.shoppingcart.entity;//购物车信息
public class CartInfo {public int id;// 商品编号public int goodsId;// 商品数量public int count;public CartInfo(){}public CartInfo(int id, int goodsId, int count) {this.id = id;this.goodsId = goodsId;this.count = count;}
}
package com.example.shoppingcart.entity;import com.example.shoppingcart.R;import java.util.ArrayList;public class Goodsinfo {public int id;public String name;public String description;public float price;public String picPath;public int pic;private static String[] mNameArray = {"iPhone11", "Mate30", "小米10", "OPPO Reno3", "vivo X30", "荣耀30S"};// 声明一个手机商品的描述数组private static String[] mDescArray = {"Apple iPhone11 256GB 绿色 4G全网通手机","华为 HUAWEI Mate30 8GB+256GB 丹霞橙 5G全网通 全面屏手机","小米 MI10 8GB+128GB 钛银黑 5G手机 游戏拍照手机","OPPO Reno3 8GB+128GB 蓝色星夜 双模5G 拍照游戏智能手机","vivo X30 8GB+128GB 绯云 5G全网通 美颜拍照手机","荣耀30S 8GB+128GB 蝶羽红 5G芯片 自拍全面屏手机"};// 声明一个手机商品的价格数组private static float[] mPriceArray = {6299, 4999, 3999, 2999, 2998, 2399};// 声明一个手机商品的大图数组private static int[] mPicArray = {R.drawable.iphone, R.drawable.huawei, R.drawable.xiaomi,R.drawable.oppo, R.drawable.vivo, R.drawable.rongyao};public static ArrayList<Goodsinfo> getDeafultList(){ArrayList<Goodsinfo> goodsList=new ArrayList<Goodsinfo>();for(int i=0;i<mNameArray.length;i++){Goodsinfo goodsinfo=new Goodsinfo();goodsinfo.id=i;goodsinfo.name=mNameArray[i];goodsinfo.description=mDescArray[i];goodsinfo.price=mPriceArray[i];goodsinfo.pic=mPicArray[i];goodsList.add(goodsinfo);}return goodsList;}
}
package com.example.shoppingcart.database;import android.annotation.SuppressLint;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;import com.example.shoppingcart.entity.CartInfo;
import com.example.shoppingcart.entity.Goodsinfo;import java.util.ArrayList;
import java.util.List;public class ShoppingDBHelper extends SQLiteOpenHelper {private static final String DB_NAME="shopping.db";private static final String TABLE_GOODS_INFO="goods_info";private static final String TABLE_CART_INFO="cart_info";private static final int DB_VERSION=2;private static ShoppingDBHelper databaseUserHelper=null;public ShoppingDBHelper(Context context) {super(context,DB_NAME,null,DB_VERSION);}//单例模式获取数据库实例public static ShoppingDBHelper getInstance(Context context){if(databaseUserHelper==null){databaseUserHelper=new ShoppingDBHelper(context);}return databaseUserHelper;}@Overridepublic void onCreate(SQLiteDatabase sqLiteDatabase) {String sql="";sql= "CREATE TABLE IF NOT EXISTS "+TABLE_GOODS_INFO+"(_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,name VARCHAR NOT NULL,description VARCHAR NOT NULL,price FLOAT NOT NULL,pic_path VARCHAR NOT NULL);";sqLiteDatabase.execSQL(sql);//购物车信息表sql= "CREATE TABLE IF NOT EXISTS "+TABLE_CART_INFO+"(_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,good_id INTEGER NOT NULL,count INTEGER NOT NULL);";sqLiteDatabase.execSQL(sql);}@Overridepublic void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {}public void insertGoodsinfos(List<Goodsinfo> list){SQLiteDatabase database=databaseUserHelper.getWritableDatabase();try{database.beginTransaction();for(Goodsinfo goodsinfo:list){ContentValues contentValues=new ContentValues();contentValues.put("name",goodsinfo.name);contentValues.put("description",goodsinfo.description);contentValues.put("price",goodsinfo.price);contentValues.put("pic_path",goodsinfo.picPath);database.insert(TABLE_GOODS_INFO,null,contentValues);}database.setTransactionSuccessful();}catch (Exception e){e.printStackTrace();}finally {database.endTransaction();}}public List<Goodsinfo> queryAllGoodsInfo(){String sql="SELECT *FROM "+TABLE_GOODS_INFO;List<Goodsinfo> list=new ArrayList<Goodsinfo>();SQLiteDatabase database=databaseUserHelper.getWritableDatabase();Cursor cursor=database.rawQuery(sql,null);while(cursor.moveToNext()) {Goodsinfo goodsinfo = new Goodsinfo();goodsinfo.id = cursor.getInt(0);goodsinfo.name = cursor.getString(1);goodsinfo.description = cursor.getString(2);goodsinfo.price = cursor.getFloat(3);goodsinfo.picPath = cursor.getString(4);list.add(goodsinfo);}cursor.close();return list;}public void insertCartInfo(int goodsId) {SQLiteDatabase database=databaseUserHelper.getWritableDatabase();//购物车不存在该商品,则添加一条信息CartInfo cartInfo=queryCartInfoByGoodsId(goodsId);ContentValues values=new ContentValues();values.put("good_id",goodsId);if(cartInfo==null){values.put("count",1);database.insert(TABLE_CART_INFO,null,values);}//如果购物车有该商品,则更新商品数量else{values.put("_id",cartInfo.id);values.put("count",++cartInfo.count);database.update(TABLE_CART_INFO,values,"_id=?",new String[]{String.valueOf(cartInfo.id)});}}private CartInfo queryCartInfoByGoodsId(int goodsId) {SQLiteDatabase database=databaseUserHelper.getReadableDatabase();Cursor cursor=database.query(TABLE_CART_INFO,null,"good_id=?",new String[]{String.valueOf(goodsId)},null,null,null,null);CartInfo info=null;if(cursor.moveToNext()){info=new CartInfo();info.id=cursor.getInt(0);info.goodsId=cursor.getInt(1);info.count=cursor.getInt(2);}return info;}//统计购物车商品的总数量public int countCartInfo() {SQLiteDatabase database=databaseUserHelper.getWritableDatabase();int count=0;String sql="SELECT sum(count) FROM " +TABLE_CART_INFO;@SuppressLint("Recycle") Cursor cursor=database.rawQuery(sql,null);if(cursor.moveToNext()){cursor.getInt(0);}return count;}public List<CartInfo> queryAllCartInfo() {SQLiteDatabase database=databaseUserHelper.getWritableDatabase();List<CartInfo> list=new ArrayList<CartInfo>();@SuppressLint("Recycle") Cursor cursor=database.query(TABLE_CART_INFO,null,null,null,null,null,null);while(cursor.moveToNext()){CartInfo info=new CartInfo();info.id=cursor.getInt(0);info.goodsId=cursor.getInt(1);info.count=cursor.getInt(2);list.add(info);}return list;}public Goodsinfo queryAllGoodsInfoById(int goodsId) {Goodsinfo info=null;SQLiteDatabase database=databaseUserHelper.getWritableDatabase();@SuppressLint("Recycle") Cursor cursor=database.query(TABLE_GOODS_INFO,null,"_id=?",new String[]{String.valueOf(goodsId)},null,null,null);if(cursor.moveToNext()){info = new Goodsinfo();info.id = cursor.getInt(0);info.name = cursor.getString(1);info.description = cursor.getString(2);info.price = cursor.getFloat(3);info.picPath = cursor.getString(4);}return info;}//根据商品ID删除购物车信息public void deleteCartInfoByGoodsId(int goodsId) {SQLiteDatabase database=databaseUserHelper.getWritableDatabase();database.delete(TABLE_CART_INFO,"good_id=?",new String[]{String.valueOf(goodsId)});}//删除所有购物车信息public void deleteAllCartInfo(){SQLiteDatabase database=databaseUserHelper.getWritableDatabase();database.delete(TABLE_CART_INFO,"1=1",null);}
}
Activity类主要有4个,分别为MyApplication,购物车类,商品市场类,商品详情类,MyApplication启动时自动将商品数据插入到数据库,并设置购物车全局变量为0.
商品类的展示再通过从数据库中获取存储的商品信息,通过获取商品的子视图,动态的嵌入到页面中,当点击加入购物车时,将点击的商品加入到购物车数据库,随后购物车全局变量+1,显示出来。
购物车功能实现思路:从购物车数据库中取出商品的good_id,从商品数据库中查询出商品的全部信息,然后动态嵌入子视图进行展示。清空功能则直接清空购物车数据库,并将购物车全局变量归0,长按点击删除实现思路则是对嵌入的view进行长监听,长按点击则弹窗,如果删除则从购物车数据库中删除,并将view子视图移除,Myapplication全局变量-1。
商品详情页实现思路:利用Intent传一个商品的good_id进入到商品详情页,通过good_id查询商品的信息,并赋到控件中展示。
MyApplication类:
package com.example.shoppingcart;import android.app.Application;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.util.Log;import com.example.shoppingcart.database.ShoppingDBHelper;
import com.example.shoppingcart.entity.Goodsinfo;
import com.example.shoppingcart.util.FileUtil;
import com.example.shoppingcart.util.SharedUtil;import java.io.File;
import java.util.List;public class MyApplication extends Application {private static MyApplication mApp;public int goodsCount;public static MyApplication getInstance(){return mApp;}public void onCreate() {super.onCreate();mApp=this;Log.d("App状态:","App启动");InitGoodsInfo();}private void InitGoodsInfo() {boolean isFirst= SharedUtil.getInstance(this).readBoolean("first",true);String directory=getExternalFilesDir((Environment.DIRECTORY_DOWNLOADS)).toString()+ File.separatorChar;if(isFirst){List<Goodsinfo> list=Goodsinfo.getDeafultList();for(Goodsinfo goodsinfo:list){Bitmap bitmap= BitmapFactory.decodeResource(getResources(),goodsinfo.pic);String path=directory+goodsinfo.id+".jpg";FileUtil.saveImage(path,bitmap);bitmap.recycle();goodsinfo.picPath=path;}//打开数据库将商品信息插入表中ShoppingDBHelper shoppingDBHelper=ShoppingDBHelper.getInstance(this);shoppingDBHelper.getWritableDatabase();shoppingDBHelper.insertGoodsinfos(list);shoppingDBHelper.close();SharedUtil.getInstance(this).writeBoolean("first",false);}}
}
商品市场类:
package com.example.shoppingcart;import androidx.appcompat.app.AppCompatActivity;import android.annotation.SuppressLint;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.GridLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;import com.example.shoppingcart.database.ShoppingDBHelper;
import com.example.shoppingcart.entity.Goodsinfo;
import com.example.shoppingcart.util.ToastUtil;import java.util.List;public class ShoppingChannelActivity extends AppCompatActivity implements View.OnClickListener{private ShoppingDBHelper shoppingDBHelper;private TextView tv_count;private GridLayout gl_Channel;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_shopping_channel);shoppingDBHelper=ShoppingDBHelper.getInstance(this);shoppingDBHelper.getWritableDatabase();shoppingDBHelper.getReadableDatabase();TextView tv_title=findViewById(R.id.tv_title);tv_title.setText("手机商场");tv_count=findViewById(R.id.tv_count);gl_Channel=findViewById(R.id.gl_channel);findViewById(R.id.iv_back).setOnClickListener(this);findViewById(R.id.iv_cart).setOnClickListener(this);showGoods();}//通过手机视图根文件的形式,动态查询数据库,并嵌入到页面中private void showGoods() {//获取屏幕宽度int screenWidth=getResources().getDisplayMetrics().widthPixels;LinearLayout.LayoutParams params=new LinearLayout.LayoutParams(screenWidth/2,LinearLayout.LayoutParams.WRAP_CONTENT);//查询数据库所有商品记录List<Goodsinfo> list=shoppingDBHelper.queryAllGoodsInfo();gl_Channel.removeAllViews();for(Goodsinfo goodsinfo:list){//获取布局文件item_goods.xml的根视图@SuppressLint("InflateParams") View view= LayoutInflater.from(this).inflate(R.layout.item_goods,null);ImageView iv_thumb=view.findViewById(R.id.iv_thumb);TextView tv_name=view.findViewById(R.id.tv_name);TextView tv_price=view.findViewById(R.id.tv_price);Button btn_add=view.findViewById(R.id.btn_add);iv_thumb.setImageURI(Uri.parse(goodsinfo.picPath));tv_name.setText(goodsinfo.name);tv_price.setText(String.valueOf((int) goodsinfo.price));btn_add.setOnClickListener(v->{addToCart(goodsinfo.id,goodsinfo.name);});iv_thumb.setOnClickListener(v->{Intent intent=new Intent(ShoppingChannelActivity.this,ShoppingDetailActivity.class);intent.putExtra("good_id",goodsinfo.id);startActivity(intent);});//把商品视图加到网格布局,设置屏幕的宽高gl_Channel.addView(view,params);}}private void addToCart(int goodsId,String name){shoppingDBHelper.insertCartInfo(goodsId);int count=++MyApplication.getInstance().goodsCount;tv_count.setText(String.valueOf(count));ToastUtil.show(this,"已添加一部"+name+"到购物车");}@Overrideprotected void onResume() {super.onResume();Log.d("返回了","返回了页面");//查询商品总数并展示showCartInfoTotal();}private void showCartInfoTotal() {int count=shoppingDBHelper.countCartInfo();MyApplication.getInstance().goodsCount=count;Log.d("count:", String.valueOf(count));tv_count.setText(String.valueOf(count));}@Overrideprotected void onDestroy() {super.onDestroy();shoppingDBHelper.close();}@SuppressLint("NonConstantResourceId")@Overridepublic void onClick(View view) {switch (view.getId()){case R.id.iv_back:finish();break;case R.id.iv_cart:Intent intent=new Intent(this,ShoppingcartActivity.class);intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);startActivity(intent);break;}}
}
购物车类:
package com.example.shoppingcart;import androidx.appcompat.app.AppCompatActivity;import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;import com.example.shoppingcart.database.ShoppingDBHelper;
import com.example.shoppingcart.entity.CartInfo;
import com.example.shoppingcart.entity.Goodsinfo;
import com.example.shoppingcart.util.ToastUtil;import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;public class ShoppingcartActivity extends AppCompatActivity implements View.OnClickListener {private TextView tv_count;private LinearLayout ll_cart;private ShoppingDBHelper shoppingDBHelper;private List<CartInfo> mCartList;private TextView tv_total_price;private LinearLayout ll_empty;private LinearLayout ll_content;//声明一个根据商品编号查找商品信息的映射,把商品信息缓存起来private final Map<Integer,Goodsinfo> goodsinfoMap=new HashMap<>();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_shoppingcart);tv_count=findViewById(R.id.tv_count);TextView tv_title=findViewById(R.id.tv_title);tv_title.setText("购物车");ll_cart=findViewById(R.id.ll_cart);tv_total_price=findViewById(R.id.tv_total_price);findViewById(R.id.iv_back).setOnClickListener(this);findViewById(R.id.btn_shopping_channel).setOnClickListener(this);findViewById(R.id.btn_clear).setOnClickListener(this);findViewById(R.id.btn_settle).setOnClickListener(this);tv_count.setText(String.valueOf(MyApplication.getInstance().goodsCount));shoppingDBHelper=ShoppingDBHelper.getInstance(this);ll_empty=findViewById(R.id.ll_empty);ll_content=findViewById(R.id.ll_content);}@Overrideprotected void onResume() {super.onResume();showCart();}//展示购物车中的商品列表private void showCart() {//移除下面所有子视图ll_cart.removeAllViews();mCartList=shoppingDBHelper.queryAllCartInfo();if(mCartList.size()==0){return ;}for(CartInfo info:mCartList ){//根据商品编号查询商品数据库中的记录Goodsinfo goods=shoppingDBHelper.queryAllGoodsInfoById(info.goodsId);goodsinfoMap.put(info.goodsId,goods);View view=LayoutInflater.from(this).inflate(R.layout.item_cart,null);ImageView iv_thumb=view.findViewById(R.id.iv_thumb);TextView tv_name=view.findViewById(R.id.tv_name);TextView tv_desc=view.findViewById(R.id.tv_desc);TextView tv_count=view.findViewById(R.id.tv_price);@SuppressLint("CutPasteId") TextView tv_price=view.findViewById(R.id.tv_price);TextView tv_sum=view.findViewById(R.id.tv_sum);iv_thumb.setImageURI(Uri.parse(goods.picPath));tv_name.setText(goods.name);tv_desc.setText(goods.description);tv_count.setText(String.valueOf(info.count));tv_price.setText(String.valueOf((int) goods.price));tv_sum.setText(String.valueOf((int) (info.count*goods.price)));//给商品行添加长按事件,长按商品就删除该商品view.setOnLongClickListener(new View.OnLongClickListener() {@Overridepublic boolean onLongClick(final View v) {AlertDialog.Builder builder = new AlertDialog.Builder(ShoppingcartActivity.this);builder.setMessage("是否从购物车删除"+goods.name+"?");builder.setPositiveButton("是", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {ll_cart.removeView(v); // 移除当前视图deleteGoods(info); // 删除该商品}});builder.setNegativeButton("否", null);builder.create().show(); // 显示提醒对话框return true;}});view.setOnClickListener(v->{Intent intent = new Intent(ShoppingcartActivity.this, ShoppingDetailActivity.class);intent.putExtra("good_id", goods.id);startActivity(intent);});ll_cart.addView(view);}//重新计算购物车商品总金额refreshTotalPrice();}private void deleteGoods(CartInfo info) {MyApplication.getInstance().goodsCount-=info.count;//从购物车数据库中删除商品shoppingDBHelper.deleteCartInfoByGoodsId(info.goodsId);//从购物车列表中删除商品CartInfo removed=null;for(CartInfo cartInfo:mCartList){if(cartInfo.goodsId==info.goodsId){removed=cartInfo;break;}}Log.d("delete执行:","执行");mCartList.remove(removed);//显示最新商品数量showCount();ToastUtil.show(this,"已从购物车中删除"+ Objects.requireNonNull(goodsinfoMap.get(info.goodsId)).name);goodsinfoMap.remove(info.goodsId);refreshTotalPrice();}private void showCount() {tv_count.setText(String.valueOf(MyApplication.getInstance().goodsCount));//购物车中没有商品,显示空空如也if(MyApplication.getInstance().goodsCount==0){ll_empty.setVisibility(View.VISIBLE);ll_content.setVisibility(View.GONE);ll_cart.removeAllViews();}else{ll_content.setVisibility(View.VISIBLE);ll_empty.setVisibility(View.GONE);}}private void refreshTotalPrice() {int totalPrice=0;for(CartInfo info:mCartList){Goodsinfo goods=goodsinfoMap.get(info.goodsId);totalPrice+=goods.price*info.count;}tv_total_price.setText(String.valueOf(totalPrice));}@SuppressLint("NonConstantResourceId")@Overridepublic void onClick(View view) {switch (view.getId()){case R.id.iv_back:finish();break;case R.id.btn_shopping_channel:Intent intent=new Intent(this,ShoppingChannelActivity.class);intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);startActivity(intent);break;case R.id.btn_clear:shoppingDBHelper.deleteAllCartInfo();MyApplication.getInstance().goodsCount=0;showCount();break;case R.id.btn_settle:AlertDialog.Builder builder=new AlertDialog.Builder(this);builder.setTitle("结算商品");builder.setMessage("支付功能尚未开通");builder.setPositiveButton("我知道了",null);builder.create().show();break;}}
}
商品详情页类:
package com.example.shoppingcart;import androidx.appcompat.app.AppCompatActivity;import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;import com.example.shoppingcart.database.ShoppingDBHelper;
import com.example.shoppingcart.entity.Goodsinfo;
import com.example.shoppingcart.util.ToastUtil;import org.w3c.dom.Text;public class ShoppingDetailActivity extends AppCompatActivity implements View.OnClickListener {private TextView tv_count;private TextView tv_goods_price;private TextView tv_goods_desc;private ImageView tv_goods_pic;private ShoppingDBHelper shoppingDBHelper;private int mGoodsId;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_shopping_detail);tv_count=findViewById(R.id.tv_count);tv_goods_desc=findViewById(R.id.tv_goods_desc);tv_goods_price=findViewById(R.id.tv_goods_price);tv_goods_pic=findViewById(R.id.iv_goods_pic);TextView tv_title=findViewById(R.id.tv_title);tv_title.setText("商品详情");findViewById(R.id.iv_back).setOnClickListener(this);findViewById(R.id.iv_cart).setOnClickListener(this);findViewById(R.id.btn_add_cart).setOnClickListener(this);shoppingDBHelper=ShoppingDBHelper.getInstance(this);}@Overrideprotected void onResume() {super.onResume();showDetail();}private void showDetail() {//获取传来的商品编号,通过商品编号来展示页面mGoodsId=getIntent().getIntExtra("good_id",0);if(mGoodsId>0){Goodsinfo goodsinfo=shoppingDBHelper.queryAllGoodsInfoById(mGoodsId);tv_goods_desc.setText(goodsinfo.description);tv_goods_price.setText(String.valueOf(goodsinfo.price));tv_goods_pic.setImageURI(Uri.parse(goodsinfo.picPath));}}@Overridepublic void onClick(View view) {switch (view.getId()){case R.id.iv_back:finish();break;case R.id.iv_cart:Intent intent = new Intent(this, ShoppingcartActivity.class);startActivity(intent);break;case R.id.btn_add_cart:addToCart(mGoodsId);break;}}private void addToCart(int mGoodsId) {int count = ++MyApplication.getInstance().goodsCount;tv_count.setText(String.valueOf(count));shoppingDBHelper.insertCartInfo(mGoodsId);ToastUtil.show(this, "成功添加至购物车");}
}
商品展示视图:
<?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:background="@color/orange"android:orientation="vertical" ><include layout="@layout/title_shopping"/><ScrollViewandroid:layout_width="match_parent"android:layout_height="wrap_content"><GridLayoutandroid:id="@+id/gl_channel"android:layout_width="match_parent"android:layout_height="wrap_content"android:columnCount="2"/></ScrollView></LinearLayout>
购物车展示视图:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@color/orange"android:orientation="vertical"><include layout="@layout/title_shopping" /><ScrollViewandroid:layout_width="match_parent"android:layout_height="wrap_content"><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><LinearLayoutandroid:id="@+id/ll_content"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"android:visibility="visible"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><TextViewandroid:layout_width="85dp"android:layout_height="wrap_content"android:gravity="center"android:text="图片"android:textColor="@color/black"android:textSize="15sp" /><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="3"android:gravity="center"android:text="名称"android:textColor="@color/black"android:textSize="15sp" /><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:gravity="center"android:text="数量"android:textColor="@color/black"android:textSize="15sp" /><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:gravity="center"android:text="单价"android:textColor="@color/black"android:textSize="15sp" /><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:gravity="center"android:text="总价"android:textColor="@color/black"android:textSize="15sp" /></LinearLayout><LinearLayoutandroid:id="@+id/ll_cart"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:padding="0dp"><Buttonandroid:id="@+id/btn_clear"android:layout_width="wrap_content"android:layout_height="wrap_content"android:gravity="center"android:text="清空"android:textColor="@color/black"android:textSize="17sp" /><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:gravity="center|right"android:text="总金额:"android:textColor="@color/black"android:textSize="17sp" /><TextViewandroid:id="@+id/tv_total_price"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginRight="10dp"android:gravity="center|left"android:textColor="@color/red"android:textSize="25sp" /><Buttonandroid:id="@+id/btn_settle"android:layout_width="wrap_content"android:layout_height="wrap_content"android:gravity="center"android:text="结算"android:textColor="@color/black"android:textSize="17sp" /></LinearLayout></LinearLayout><LinearLayoutandroid:id="@+id/ll_empty"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"android:visibility="gone"tools:visibility="visible"><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="100dp"android:layout_marginBottom="100dp"android:gravity="center"android:text="哎呀,购物车空空如也,快去选购商品吧"android:textColor="@color/black"android:textSize="17sp" /><Buttonandroid:id="@+id/btn_shopping_channel"android:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:text="逛逛手机商场"android:textColor="@color/black"android:textSize="17sp" /></LinearLayout></RelativeLayout></ScrollView></LinearLayout>
商品详情视图:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@color/orange"android:orientation="vertical"><include layout="@layout/title_shopping" /><ScrollViewandroid:layout_width="match_parent"android:layout_height="wrap_content"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><ImageViewandroid:id="@+id/iv_goods_pic"android:layout_width="match_parent"android:layout_height="350dp"android:scaleType="fitCenter"tools:src="@drawable/xiaomi" /><TextViewandroid:id="@+id/tv_goods_price"android:layout_width="match_parent"android:layout_height="wrap_content"android:paddingLeft="5dp"android:textColor="@color/red"android:textSize="22sp"tools:text="1990" /><TextViewandroid:id="@+id/tv_goods_desc"android:layout_width="match_parent"android:layout_height="wrap_content"android:paddingLeft="5dp"android:textColor="@color/black"android:textSize="15sp"tools:text="小米 MI10 8GB+128GB 钛银黑 5G手机 游戏拍照手机" /><Buttonandroid:id="@+id/btn_add_cart"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="加入购物车"android:textColor="@color/black"android:textSize="17sp" /></LinearLayout></ScrollView></LinearLayout>
单一商品视图:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/ll_item"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:background="@color/white"android:gravity="center"android:orientation="vertical"><TextViewandroid:id="@+id/tv_name"android:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:textColor="@color/black"android:textSize="17sp"tools:text="小米手机" /><ImageViewandroid:id="@+id/iv_thumb"android:layout_width="180dp"android:layout_height="150dp"android:scaleType="fitCenter"tools:src="@drawable/xiaomi" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="45dp"android:orientation="horizontal"><TextViewandroid:id="@+id/tv_price"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="2"android:gravity="center"android:textColor="@color/red"android:textSize="15sp"tools:text="20" /><Buttonandroid:id="@+id/btn_add"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="3"android:gravity="center"android:text="加入购物车"android:textColor="@color/black"android:textSize="15sp" /></LinearLayout></LinearLayout>
购物车标志视图:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="50dp"android:background="#aaaaff"><ImageViewandroid:id="@+id/iv_back"android:layout_width="50dp"android:layout_height="match_parent"android:layout_alignParentLeft="true"android:padding="10dp"android:scaleType="fitCenter"android:src="@drawable/ic_back"/><TextViewandroid:id="@+id/tv_title"android:layout_width="wrap_content"android:layout_height="match_parent"android:layout_centerInParent="true"android:gravity="center"android:textColor="@color/black"android:textSize="20sp" /><ImageViewandroid:id="@+id/iv_cart"android:layout_width="50dp"android:layout_height="match_parent"android:layout_alignParentRight="true"android:scaleType="fitCenter"android:src="@drawable/cart" /><TextViewandroid:id="@+id/tv_count"android:layout_width="20dp"android:layout_height="20dp"android:layout_alignParentTop="true"android:layout_toRightOf="@+id/iv_cart"android:layout_marginLeft="-20dp"android:gravity="center"android:background="@drawable/shape_oval_red"android:text="0"android:textColor="@color/white"android:textSize="15sp" />
</RelativeLayout>
单一商品子视图:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/ll_item"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:background="@color/white"android:gravity="center"android:orientation="vertical"><TextViewandroid:id="@+id/tv_name"android:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:textColor="@color/black"android:textSize="17sp"tools:text="小米手机" /><ImageViewandroid:id="@+id/iv_thumb"android:layout_width="180dp"android:layout_height="150dp"android:scaleType="fitCenter"tools:src="@drawable/xiaomi" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="45dp"android:orientation="horizontal"><TextViewandroid:id="@+id/tv_price"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="2"android:gravity="center"android:textColor="@color/red"android:textSize="15sp"tools:text="20" /><Buttonandroid:id="@+id/btn_add"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="3"android:gravity="center"android:text="加入购物车"android:textColor="@color/black"android:textSize="15sp" /></LinearLayout></LinearLayout>
购物车展示单一商品子视图:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@color/white"android:orientation="horizontal"><ImageViewandroid:id="@+id/iv_thumb"android:layout_width="85dp"android:layout_height="85dp"android:scaleType="fitCenter"tools:src="@drawable/xiaomi"/><LinearLayoutandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="3"android:orientation="vertical"><TextViewandroid:id="@+id/tv_name"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="2"android:gravity="left|center"android:textColor="@color/black"android:textSize="17sp"tools:text="小米手机"/><TextViewandroid:id="@+id/tv_desc"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="3"android:gravity="left|center"android:textColor="@color/black"android:textSize="12sp"tools:text="小米 MI10 8GB+128GB 钛银黑 5G手机 游戏拍照手机"/></LinearLayout><TextViewandroid:id="@+id/tv_count"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:gravity="center"android:textColor="@color/black"android:textSize="17sp"tools:text="2"/><TextViewandroid:id="@+id/tv_price"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:gravity="right|center"android:textColor="@color/black"android:textSize="15sp"tools:text="1000"/><TextViewandroid:id="@+id/tv_sum"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1.2"android:gravity="right|center"android:textColor="@color/red"android:textSize="17sp"tools:text="2000"/></LinearLayout>
十二、内容提供者Provider
为App存取内部数据提供统一的外部接口,让不同应用之间得以共享数据,将用户的输入内容,通过ContentProvider跨进程通信传递到Server App,主要通过Uri作为地址传输,客户端通过ProviderResolver类进行操作,Uri格式如下:
content://authority/data_path/id
content:// 通用前缀,标识Uri用于ContentProvider定位资源
authority 授权者名称,用于确定具体由哪一个ContentProvider提供资源
data_path 数据路径
id 数据编号,用来请求单条数据
例子:创建server和client端,从client端操作server端的数据库,达到添加,查询,删除用户功能
Server: DatabaseUserHelper.java
package com.example.server_provider.database;import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;import java.util.ArrayList;
import java.util.List;public class DatabaseUserHelper extends SQLiteOpenHelper {private static final String DB_NAME="user.db";public static final String TABLE_NAME="user_info";private static final int DB_VERSION=2;private static DatabaseUserHelper databaseUserHelper=null;public DatabaseUserHelper(Context context) {super(context,DB_NAME,null,DB_VERSION);}//单例模式获取数据库实例public static DatabaseUserHelper getInstance(Context context){if(databaseUserHelper==null){databaseUserHelper=new DatabaseUserHelper(context);}return databaseUserHelper;}@Overridepublic void onCreate(SQLiteDatabase sqLiteDatabase) {String sql= "CREATE TABLE IF NOT EXISTS "+TABLE_NAME+"(_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,name VARCHAR NOT NULL,age INTEGER NOT NULL,height LONG NOT NULL,weight float NOT NULL,married INTEGER NOT NULL);";sqLiteDatabase.execSQL(sql);}@Overridepublic void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {}
}
provider类:
package com.example.server_provider.provider;import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.util.Log;import com.example.server_provider.database.DatabaseUserHelper;public class UserInfoProvider extends ContentProvider {private DatabaseUserHelper databaseUserHelper;public static final String AUTHORITIES = "com.example.server_provider.provider.UserInfoProvider";private static final UriMatcher URI_MATCHER=new UriMatcher(UriMatcher.NO_MATCH);private static final int USERS=1;private static final int USER=2;static {//Uri匹配器URI_MATCHER.addURI(AUTHORITIES,"/user",USERS);URI_MATCHER.addURI(AUTHORITIES,"user/#",USER);}@Overridepublic int delete(Uri uri, String selection, String[] selectionArgs) {int count=0;switch (URI_MATCHER.match(uri)){case USERS:SQLiteDatabase db1=databaseUserHelper.getWritableDatabase();count=db1.delete(DatabaseUserHelper.TABLE_NAME,selection,selectionArgs);db1.close();break;case USER:String id=uri.getLastPathSegment();SQLiteDatabase db2=databaseUserHelper.getWritableDatabase();count=db2.delete(DatabaseUserHelper.TABLE_NAME,"_id=?",new String[]{id});db2.close();break;}return count;}@Overridepublic String getType(Uri uri) {// TODO: Implement this to handle requests for the MIME type of the data// at the given URI.throw new UnsupportedOperationException("Not yet implemented");}@Overridepublic Uri insert(Uri uri, ContentValues values) {if(URI_MATCHER.match(uri)==USERS) {SQLiteDatabase db = databaseUserHelper.getWritableDatabase();db.insert(DatabaseUserHelper.TABLE_NAME, null, values);}return uri;}@Overridepublic boolean onCreate() {Log.d("Provider","Provider启动");databaseUserHelper=DatabaseUserHelper.getInstance(getContext());return true;}@Overridepublic Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {if(URI_MATCHER.match(uri)==USERS) {SQLiteDatabase db = databaseUserHelper.getWritableDatabase();return db.query(DatabaseUserHelper.TABLE_NAME, projection, selection, selectionArgs, null, null, null);}return null;}@Overridepublic int update(Uri uri, ContentValues values, String selection,String[] selectionArgs) {// TODO: Implement this to handle requests to update one or more rows.throw new UnsupportedOperationException("Not yet implemented");}
}
client端操作类:
package com.example.client_provider;import androidx.appcompat.app.AppCompatActivity;import android.annotation.SuppressLint;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;import com.example.client_provider.entity.User;
import com.example.client_provider.util.ToastUtil;public class ContentWriteActivity extends AppCompatActivity implements View.OnClickListener {private EditText name;private EditText age;private EditText weight;private EditText height;private CheckBox married;private Button save;private Button delete;private Button query;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_content_write);name=findViewById(R.id.name);age=findViewById(R.id.age);weight=findViewById(R.id.weight);height=findViewById(R.id.height);married=findViewById(R.id.married);save=findViewById(R.id.save);delete=findViewById(R.id.delete);query=findViewById(R.id.query);save.setOnClickListener(this);delete.setOnClickListener(this);query.setOnClickListener(this);}@SuppressLint({"NonConstantResourceId", "Range"})@Overridepublic void onClick(View view) {switch (view.getId()){case R.id.save:ContentValues values=new ContentValues();values.put(UserInfoContent.NAME,name.getText().toString());values.put(UserInfoContent.AGE,Integer.parseInt(age.getText().toString()));values.put(UserInfoContent.HEIGHT,height.getText().toString());values.put(UserInfoContent.WEIGHT,weight.getText().toString());values.put(UserInfoContent.MARRIED,married.isChecked());getContentResolver().insert(UserInfoContent.CONTENT_Uri,values);ToastUtil.show(this,"保存成功");break;case R.id.query:Cursor cursor=getContentResolver().query(UserInfoContent.CONTENT_Uri,null,null,null,null);if(cursor!=null){while (cursor.moveToNext()){User info=new User();info.id=cursor.getInt(cursor.getColumnIndex(UserInfoContent._ID));info.name=cursor.getString(cursor.getColumnIndex(UserInfoContent.NAME));info.age=cursor.getInt(cursor.getColumnIndex(UserInfoContent.AGE));info.height=cursor.getInt(cursor.getColumnIndex(UserInfoContent.HEIGHT));info.weight=cursor.getFloat(cursor.getColumnIndex(UserInfoContent.WEIGHT));info.married= cursor.getInt(cursor.getColumnIndex(UserInfoContent.MARRIED)) == 1;Log.d("查询结果:",info.toString());}cursor.close();}break;case R.id.delete://删除id为2的Uri uri= Uri.parse("content://com.example.server_provider.provider.UserInfoProvider/user/2");//删除全int count = getContentResolver().delete(UserInfoContent.CONTENT_Uri, "name=?", new String[]{"Aiwin"});if(count>0){ToastUtil.show(this,"删除成功");}break;}}
}
- 运行时动态申请权限
Android系统未了防止某些App滥用权限,从6.0开始引入运行时权限管理机制,允许App在允许过程中拥有某项权限,一旦缺少,可自动弹出小窗口提示开启权限,
也可在应用启动时候即申请全部权限,一般流程如下:
- 检查App是否开启指定权限,调用ContextCompat的checkSelfPermission方法
- 请求系统弹窗,便于用户选择,调用ActivityCompat的requestPermissions方法,命令系统弹出申请窗口
- 重写获得页面的权限请求回调方法onRequestPermissionsResult,在该方法内部处理用户权限选择结果。
例子:申请短信读写、联系人读写权限
package com.example.client_provider;import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.view.View;import com.example.client_provider.util.PermissionUtil;
import com.example.client_provider.util.ToastUtil;public class PermissionHungryActivity extends AppCompatActivity implements View.OnClickListener{private static final int RequestAll=0;private static final String[] PERMISSION_ALL=new String[]{Manifest.permission.READ_CONTACTS,Manifest.permission.WRITE_CONTACTS,Manifest.permission.READ_SMS,Manifest.permission.SEND_SMS};private static final String[] PERMISSION_CONTACT=new String[]{Manifest.permission.READ_CONTACTS,Manifest.permission.WRITE_CONTACTS};private static final int RequestCodeConTact=1;private static final String[] PERMISSION_SMS=new String[]{Manifest.permission.READ_SMS,Manifest.permission.SEND_SMS};private static final int RequestCodeSMS=2;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_permission_lazy);findViewById(R.id.btn_contact).setOnClickListener(this);findViewById(R.id.btn_sms).setOnClickListener(this);PermissionUtil.CheckPermissions(this,PERMISSION_ALL,RequestAll);}@SuppressLint("NonConstantResourceId")@Overridepublic void onClick(View view) {switch (view.getId()){case R.id.btn_contact:PermissionUtil.CheckPermissions(this,PERMISSION_CONTACT,RequestCodeConTact);break;case R.id.btn_sms:PermissionUtil.CheckPermissions(this,PERMISSION_SMS,RequestCodeSMS);break;}}@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);switch (requestCode){case RequestAll:if(PermissionUtil.checkGrant(grantResults)){Log.d("权限情况","所有权限获取成功");}else{for(int i=0;i<permissions.length;i++){if(grantResults[i]!= PackageManager.PERMISSION_GRANTED){switch (permissions[i]){case Manifest.permission.READ_CONTACTS:case Manifest.permission.WRITE_CONTACTS:ToastUtil.show(this,"通讯录读写获取不成功");JumpToSettings();return ;case Manifest.permission.READ_SMS:case Manifest.permission.SEND_SMS:ToastUtil.show(this,"短信读写获取不成功");JumpToSettings();return ;}}}}case RequestCodeConTact:if(PermissionUtil.checkGrant(grantResults)){Log.d("权限情况:","通讯录权限请求成功");}else{ToastUtil.show(this,"通讯录获取失败");JumpToSettings();}break;case RequestCodeSMS:if(PermissionUtil.checkGrant(grantResults)){Log.d("权限情况:","短信权限请求成功");}else{ToastUtil.show(this,"短信获取失败");JumpToSettings();}break;}}public void JumpToSettings(){Intent intent=new Intent();intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);intent.setData(Uri.fromParts("package", getPackageName(), null));intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);startActivity(intent);}
}
package com.example.client_provider.util;import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Build;import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;public class PermissionUtil {public static boolean CheckPermissions(Activity activity, String[] permissions, int RequestCode){if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.M){int check= PackageManager.PERMISSION_GRANTED;for(String permission:permissions ){check=ContextCompat.checkSelfPermission(activity,permission);if(check!=PackageManager.PERMISSION_GRANTED){break;}}//未开启权限则自动弹窗if(check!=PackageManager.PERMISSION_GRANTED){ActivityCompat.requestPermissions(activity,permissions,RequestCode);return false;}}return true;}public static boolean checkGrant(int[] grantResults) {if(grantResults!=null){for(int grantResult :grantResults){if(grantResult!=PackageManager.PERMISSION_GRANTED){return false;}}return true;}return false;}}
- 添加查询联系人
利用ContentResolver读写联系人,联系人分为两张表:
raw_contacts表:记录联系人的_id号,状态等信息
data表:记录用户通讯录的所有数据,根据mimetype_id表示不同的数据类型,raw_contact_id与raw_contacts表中的_id对应,根据这种外键联系,
一个联系人可添加多个邮箱,多个电话号码。
例子:添加和查询联系人,前提:权限已开启
package com.example.client_provider;import androidx.appcompat.app.AppCompatActivity;import android.annotation.SuppressLint;
import android.content.ContentProvider;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.CommonDataKinds;
import com.example.client_provider.entity.Contact;import java.util.ArrayList;public class ContactAddActivity extends AppCompatActivity implements View.OnClickListener {private EditText et_contact_name;private EditText et_contact_phone;private EditText et_contact_email;@SuppressLint({"WrongViewCast", "MissingInflatedId"})@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_contact_add);et_contact_name=findViewById(R.id.tv_name);et_contact_phone=findViewById(R.id.phone);et_contact_email=findViewById(R.id.email);findViewById(R.id.add).setOnClickListener(this);findViewById(R.id.query).setOnClickListener(this);}@SuppressLint("NonConstantResourceId")@Overridepublic void onClick(View view) {switch (view.getId()){case R.id.add:Contact contact=new Contact();contact.name=et_contact_name.getText().toString();contact.phone=et_contact_phone.getText().toString();contact.email=et_contact_email.getText().toString();//使用ContentSolver多次写入//addContacts(getContentResolver(),contact);//批量处理联系人,好处是,要么全部成功,要么全部失败,保证了事务的一致性addFullContacts(getContentResolver(), contact);break;case R.id.query:readPhoneContact(getContentResolver());break;}}private void readPhoneContact(ContentResolver resolver) {Cursor cursor = resolver.query(ContactsContract.RawContacts.CONTENT_URI, new String[]{ContactsContract.RawContacts._ID}, null, null, null, null);while (cursor.moveToNext()) {int rawContactId = cursor.getInt(0);Uri uri = Uri.parse("content://com.android.contacts/contacts/" + rawContactId + "/data");Cursor dataCursor = resolver.query(uri, new String[]{Contacts.Data.MIMETYPE, Contacts.Data.DATA1, Contacts.Data.DATA2},null, null, null);Contact contact = new Contact();while (dataCursor.moveToNext()) {@SuppressLint("Range") String data1 = dataCursor.getString(dataCursor.getColumnIndex(Contacts.Data.DATA1));@SuppressLint("Range") String mimeType = dataCursor.getString(dataCursor.getColumnIndex(Contacts.Data.MIMETYPE));switch (mimeType) {case CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE:contact.name = data1;break;case CommonDataKinds.Email.CONTENT_ITEM_TYPE:contact.email = data1;break;case CommonDataKinds.Phone.CONTENT_ITEM_TYPE:contact.phone = data1;break;}}dataCursor.close();if (contact.name != null) {Log.d("联系人:", contact.toString());}}cursor.close();}private void addFullContacts(ContentResolver contentResolver, Contact contact) {ContentProviderOperation op_main=ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI).withValue(ContactsContract.RawContacts.ACCOUNT_NAME,null).build();//联系人姓名记录操作器ContentProviderOperation op_name = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)// 将第0个操作的id,即 raw_contacts 的 id 作为 data 表中的 raw_contact_id.withValueBackReference(Contacts.Data.RAW_CONTACT_ID, 0).withValue(Contacts.Data.MIMETYPE, CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE).withValue(Contacts.Data.DATA2, contact.name).build();ContentProviderOperation op_phone=ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).withValueBackReference(Contacts.Data.RAW_CONTACT_ID,0).withValue(Contacts.Data.MIMETYPE,CommonDataKinds.Phone.CONTENT_ITEM_TYPE).withValue(Contacts.Data.DATA1, contact.email).withValue(Contacts.Data.DATA2, CommonDataKinds.Email.TYPE_WORK).build();ContentProviderOperation op_email = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)// 将第0个操作的id,即 raw_contacts 的 id 作为 data 表中的 raw_contact_id.withValueBackReference(Contacts.Data.RAW_CONTACT_ID, 0).withValue(Contacts.Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE).withValue(Contacts.Data.DATA1, contact.email).withValue(Contacts.Data.DATA2, CommonDataKinds.Email.TYPE_WORK).build();ArrayList<ContentProviderOperation> operations = new ArrayList<>();operations.add(op_main);operations.add(op_name);operations.add(op_phone);operations.add(op_email);try {contentResolver.applyBatch(ContactsContract.AUTHORITY,operations);} catch (OperationApplicationException | RemoteException e) {throw new RuntimeException(e);}}private void addContacts(ContentResolver resolver, Contact contact) {ContentValues values=new ContentValues();Uri uri=resolver.insert(ContactsContract.RawContacts.CONTENT_URI,values);//获取RawContacts表中的IDlong rawContactId= ContentUris.parseId(uri);ContentValues name=new ContentValues();//关联联系人编号name.put(Contacts.Data.RAW_CONTACT_ID,rawContactId);//姓名的数据类型name.put(Contacts.Data.MIMETYPE,CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);//联系人姓名name.put(Contacts.Data.DATA2,contact.name);resolver.insert(ContactsContract.Data.CONTENT_URI,name);ContentValues phone=new ContentValues();phone.put(Contacts.Data.RAW_CONTACT_ID,rawContactId);phone.put(Contacts.Data.MIMETYPE,CommonDataKinds.Phone.CONTENT_ITEM_TYPE);phone.put(Contacts.Data.DATA1,contact.phone);//联系类型,1表示家庭,2表示工作phone.put(Contacts.Data.DATA2,CommonDataKinds.Phone.TYPE_MOBILE);resolver.insert(ContactsContract.Data.CONTENT_URI, phone);ContentValues email=new ContentValues();email.put(Contacts.Data.RAW_CONTACT_ID,rawContactId);email.put(Contacts.Data.MIMETYPE,CommonDataKinds.Email.CONTENT_ITEM_TYPE);email.put(Contacts.Data.DATA1,contact.email);email.put(Contacts.Data.DATA2,CommonDataKinds.Email.TYPE_WORK);resolver.insert(ContactsContract.Data.CONTENT_URI,email);}
}