实现功能:
手机商品页面展示,加入购物车功能,商品详情页面,清空购物车,删除购物车商品。
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;
}
@Override
public 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);
}
@Override
public 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;
@Override
protected 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+"到购物车");
}
@Override
protected 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));
}
@Override
protected void onDestroy() {
super.onDestroy();
shoppingDBHelper.close();
}
@SuppressLint("NonConstantResourceId")
@Override
public 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<>();
@Override
protected 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);
}
@Override
protected 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() {
@Override
public boolean onLongClick(final View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(ShoppingcartActivity.this);
builder.setMessage("是否从购物车删除"+goods.name+"?");
builder.setPositiveButton("是", new DialogInterface.OnClickListener() {
@Override
public 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")
@Override
public 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;
@Override
protected 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);
}
@Override
protected 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));
}
}
@Override
public 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, "成功添加至购物车");
}
}
商品展示视图:
<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"/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<GridLayout
android: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" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/ll_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="visible">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="85dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="图片"
android:textColor="@color/black"
android:textSize="15sp" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:gravity="center"
android:text="名称"
android:textColor="@color/black"
android:textSize="15sp" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="数量"
android:textColor="@color/black"
android:textSize="15sp" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="单价"
android:textColor="@color/black"
android:textSize="15sp" />
<TextView
android: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>
<LinearLayout
android:id="@+id/ll_cart"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="0dp">
<Button
android: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" />
<TextView
android: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" />
<TextView
android: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" />
<Button
android: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>
<LinearLayout
android:id="@+id/ll_empty"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible">
<TextView
android: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" />
<Button
android: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" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_goods_pic"
android:layout_width="match_parent"
android:layout_height="350dp"
android:scaleType="fitCenter"
tools:src="@drawable/xiaomi" />
<TextView
android: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" />
<TextView
android: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手机 游戏拍照手机" />
<Button
android: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">
<TextView
android: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="小米手机" />
<ImageView
android:id="@+id/iv_thumb"
android:layout_width="180dp"
android:layout_height="150dp"
android:scaleType="fitCenter"
tools:src="@drawable/xiaomi" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="45dp"
android:orientation="horizontal">
<TextView
android: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" />
<Button
android: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>
购物车标志视图:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#aaaaff">
<ImageView
android: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"/>
<TextView
android: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" />
<ImageView
android:id="@+id/iv_cart"
android:layout_width="50dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:scaleType="fitCenter"
android:src="@drawable/cart" />
<TextView
android: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">
<TextView
android: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="小米手机" />
<ImageView
android:id="@+id/iv_thumb"
android:layout_width="180dp"
android:layout_height="150dp"
android:scaleType="fitCenter"
tools:src="@drawable/xiaomi" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="45dp"
android:orientation="horizontal">
<TextView
android: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" />
<Button
android: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">
<ImageView
android:id="@+id/iv_thumb"
android:layout_width="85dp"
android:layout_height="85dp"
android:scaleType="fitCenter"
tools:src="@drawable/xiaomi"/>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="3"
android:orientation="vertical">
<TextView
android: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="小米手机"/>
<TextView
android: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>
<TextView
android: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"/>
<TextView
android: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"/>
<TextView
android: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>
为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;
}
@Override
public 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);
}
@Override
public 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);
}
@Override
public 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;
}
@Override
public 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");
}
@Override
public 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;
}
@Override
public boolean onCreate() {
Log.d("Provider","Provider启动");
databaseUserHelper=DatabaseUserHelper.getInstance(getContext());
return true;
}
@Override
public 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;
}
@Override
public 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;
@Override
protected 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"})
@Override
public 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在允许过程中拥有某项权限,一旦缺少,可自动弹出小窗口提示开启权限,
也可在应用启动时候即申请全部权限,一般流程如下:
例子:申请短信读写、联系人读写权限
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;
@Override
protected 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")
@Override
public 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;
}
}
@Override
public 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"})
@Override
protected 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")
@Override
public 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表中的ID
long 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);
}
}
ContentObserver监听短信
内容观察期ContentObserver给目标内容注册一个观察器,目标内容一旦发生变化,规定好
的动作马上触发,从而执行开发者预先定义的代码
例子:
package com.example.client_provider;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
public class MonitorsmsActivity extends AppCompatActivity {
private SmsGetObserver mObserver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_monitorsms);
Uri uri=Uri.parse("content://sms");
mObserver = new SmsGetObserver(this);
//true表示会同时匹配uri的派生uri
getContentResolver().registerContentObserver(uri,true,mObserver);
}
@Override
protected void onDestroy() {
super.onDestroy();
getContentResolver().unregisterContentObserver(mObserver);
}
private static class SmsGetObserver extends ContentObserver {
private final Context mContext;
public SmsGetObserver(Context context) {
super(new Handler(Looper.getMainLooper()));
this.mContext = context;
}
@SuppressLint("Range")
@Override
public void onChange(boolean selfChange, @Nullable Uri uri) {
super.onChange(selfChange, uri);
// onChange会多次调用,收到一条短信会调用两次onChange
// mUri===content://sms/raw/20
// mUri===content://sms/inbox/20
// 安卓7.0以上系统,点击标记为已读,也会调用一次
// mUri===content://sms
// 收到一条短信都是uri后面都会有确定的一个数字,对应数据库的_id,比如上面的20
if (uri == null) {
return;
}
if (uri.toString().contains("content://sms/raw") ||
uri.toString().equals("content://sms")) {
return;
}
// 通过内容解析器获取符合条件的结果集游标
Cursor cursor = mContext.getContentResolver().query(uri, new String[]{"address", "body", "date"}, null, null, "date DESC");
if (cursor.moveToNext()) {
// 短信的发送号码
String sender = cursor.getString(cursor.getColumnIndex("address"));
// 短信内容
String content = cursor.getString(cursor.getColumnIndex("body"));
Log.d("监听到的短信:", String.format("发送方:%s,内容为:%s", sender, content));
}
cursor.close();
}
}
}
通过Intent和 registerForActivityResult跳转到系统相册选择图片后返回图片到附件,再通过Intent设置权限发送彩信。
package com.example.client_provider;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
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.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;
import com.example.client_provider.util.ToastUtil;
public class SendMmsActivity extends AppCompatActivity implements View.OnClickListener {
private ImageView iv_appendix;
private ActivityResultLauncher<Intent> mResultLauncher;
private EditText et_phone;
private EditText et_title;
private EditText et_content;
private Button btn_send;
private Uri picUri;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_send_mms);
iv_appendix=findViewById(R.id.iv_appenddix);
iv_appendix.setOnClickListener(this);
et_phone=findViewById(R.id.phone);
et_title=findViewById(R.id.title);
et_content=findViewById(R.id.content);
btn_send=findViewById(R.id.btn_send_sms);
btn_send.setOnClickListener(this);
mResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {
@Override
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() == RESULT_OK) {
Intent intent = result.getData();
picUri = intent.getData();
if (picUri != null) {
iv_appendix.setImageURI(picUri);
Log.d("选中的图片是", picUri.toString());
}
}
}
});
}
@SuppressLint("NonConstantResourceId")
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.iv_appenddix:
Intent intent=new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
mResultLauncher.launch(intent);
break;
case R.id.btn_send_sms:
sendMms(et_phone.getText().toString(),et_title.getText().toString(),
et_content.getText().toString());
break;
}
}
private void sendMms(String phone,String title,String message){
Intent intent=new Intent(Intent.ACTION_SEND);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// Intent 的接受者将被准许读取Intent 携带的URI数据
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra("address",phone);
intent.putExtra("title",title);
intent.putExtra("message",message);
intent.putExtra(Intent.EXTRA_STREAM,picUri);
intent.setType("image/*");
startActivity(intent);
ToastUtil.show(this,"请在弹窗中选择短信或者信息应用");
}
}
需求如下,发送彩信,从手机中查询6张合适的图片显示到屏幕中供用户选择,用户点击图片即可发送彩信,需通过FileProvider,将文件暴露给第三方,并授予读写权限。
<paths>
<external-path name="external_storage_download" path="Download"/>
paths>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<queries>
<provider android:authorities="com.example.server_provider.provider.UserInfoProvider" />
queries>
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.MyApplication">
<activity
android:name=".ProviderMmsAcivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
intent-filter>
activity>
<provider
android:authorities="@string/file_provider"
android:name="androidx.core.content.FileProvider"
android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
provider>
application>
manifest>
package com.example.client_provider;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.FileProvider;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.GridLayout;
import android.widget.ImageView;
import com.example.client_provider.entity.ImageInfo;
import com.example.client_provider.util.FileUtil;
import com.example.client_provider.util.PermissionUtil;
import com.example.client_provider.util.ToastUtil;
import com.example.client_provider.util.Utils;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class ProviderMmsAcivity extends AppCompatActivity {
private static final String[] PERMISSIONS = new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE
};
private static final int PERMISSION_REQUEST_CODE = 1;
private final List<ImageInfo> imageInfoList=new ArrayList<>();
private GridLayout gl_appendix;
private EditText et_phone;
private EditText et_title;
private EditText et_content;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_provider_mms_acivity);
gl_appendix=findViewById(R.id.gl_appendix);
et_phone=findViewById(R.id.phone);
et_title=findViewById(R.id.title);
et_content=findViewById(R.id.content);
MediaScannerConnection.scanFile(this,new String[]{Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString()},null,null);
if(PermissionUtil.CheckPermissions(this,PERMISSIONS,PERMISSION_REQUEST_CODE)){
loadImagelist();
showImageGrid();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode==PERMISSION_REQUEST_CODE && PermissionUtil.checkGrant(grantResults)){
loadImagelist();
showImageGrid();
}
}
private void showImageGrid() {
gl_appendix.removeAllViews();
for (ImageInfo info:imageInfoList){
ImageView iv_appendix=new ImageView(this);
Bitmap bitmap= BitmapFactory.decodeFile(info.path);
iv_appendix.setImageBitmap(bitmap);
iv_appendix.setScaleType(ImageView.ScaleType.FIT_CENTER);
int px= Utils.dip2px(this,110);
ViewGroup.LayoutParams params=new ViewGroup.LayoutParams(px,px);
iv_appendix.setLayoutParams(params);
int padding=Utils.dip2px(this,5);
iv_appendix.setPadding(padding,padding,padding,padding);
iv_appendix.setOnClickListener(v->{
sendMms(et_phone.getText().toString(),et_title.getText().toString(),
et_content.getText().toString(),info.path);
});
gl_appendix.addView(iv_appendix);
}
}
@SuppressLint("Range")
private void loadImagelist() {
String[] columns=new String[]{
MediaStore.Images.Media._ID,
MediaStore.Images.Media.TITLE,
MediaStore.Images.Media.SIZE,
MediaStore.Images.Media.DATA,
};
//根据Media获取图片的信息
@SuppressLint("Recycle") Cursor cursor=getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
columns,
"_size< 409600",
null,
"_size DESC"
);
int count=0;
if(cursor!=null) {
while(cursor.moveToNext() && count < 6) {
ImageInfo info = new ImageInfo();
info.id = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media._ID));
info.name = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.TITLE));
info.size = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media.SIZE));
info.path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
//检查文件是否合法,是否允许访问。
if(FileUtil.checkFileUri(this,info.path)){
count++;
imageInfoList.add(info);
}
Log.d("获取的图片有",info.toString());
}
}
}
@SuppressLint("StringFormatInvalid")
private void sendMms(String phone, String title, String message, String path){
Uri uri;
uri= FileProvider.getUriForFile(this,getString(R.string.file_provider),new File(path));
Intent intent=new Intent(Intent.ACTION_SEND);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// Intent 的接受者将被准许读取Intent 携带的URI数据
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra("address",phone);
intent.putExtra("title",title);
intent.putExtra("message",message);
intent.putExtra(Intent.EXTRA_STREAM,uri);
intent.setType("image/*");
startActivity(intent);
ToastUtil.show(this,"请在弹窗中选择短信或者信息应用");
}
}
要使用应用安装也要通过FileProvider提供,其中安卓11之后要额外获取权限android.permission.MANAGE_EXTERNAL_STORAGE的权限,安卓8的时候
要获取READ_EXTERNAL_STORAGE权限,因此这里要注意版本与权限对应。
例子:点击Install按钮自动安卓指定的应用
package com.example.client_provider;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.FileProvider;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.Settings;
import android.view.View;
import android.widget.Toast;
import com.example.client_provider.util.PermissionUtil;
import com.example.client_provider.util.ToastUtil;
import java.io.File;
public class ProviderApkActivity extends AppCompatActivity implements View.OnClickListener {
//安卓8以后要获取的权限
private static final String[] PERMISSIONS=new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE
};
private static final int PERMISSION_REQUEST_CODE=1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_provider_apk);
findViewById(R.id.install).setOnClickListener(this);
}
@Override
public void onClick(View view) {
//安卓11后要获取MANAGE_EXTERNAL_STORAGE权限
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.R){
checkAndInstall();
}
else {
if (PermissionUtil.CheckPermissions(this, PERMISSIONS, PERMISSION_REQUEST_CODE)) {
installApk();
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode==PERMISSION_REQUEST_CODE&& PermissionUtil.checkGrant(grantResults)){
installApk();
}
}
@RequiresApi(api = Build.VERSION_CODES.R)
private void checkAndInstall() {
if(!Environment.isExternalStorageManager()){
Intent intent=new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setData(Uri.fromParts("package",getPackageName(),null));
startActivity(intent);
}
else{
installApk();
}
}
private void installApk() {
String apkPath= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString()+"app-release.apk";
//应用包管理器
PackageManager packageManager=getPackageManager();
//获取apk的文件信息
PackageInfo info=packageManager.getPackageArchiveInfo(apkPath, PackageManager.GET_ACTIVITIES);
if(info==null){
ToastUtil.show(this,"安装文件损坏");
return;
}
Uri uri;
uri=FileProvider.getUriForFile(this,getString(R.string.file_provider),new File(apkPath));
Intent intent=new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(uri,"application/vnd.android.package-archive");
startActivity(intent);
}
}
关于高级控件的一些视图处理,会有一个总处理器Adapter,它的继承结构如下:
下拉列表有两种模式,分别为dropdown和dialog模式即弹出框,设置下拉列表的时候,直接设置子视图后转交给ArrayAdapter处理即可,
ArrayAdapter是最简单的适配器,只能显示一行文本。
例子:设置城市下拉框,监听选择的城市
package com.example.highcontrolleractivity;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import com.example.highcontrolleractivity.util.ToastUtil;
public class SpinnerDropdownActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener {
private static final String[] CityArray=new String[]{"广州","上海","北京","深圳","杭州"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_spinner_dropdown);
Spinner sp_dropdown=findViewById(R.id.sp_dropdown);
ArrayAdapter<String> stringArrayAdapter=new ArrayAdapter<>(this,R.layout.item_selector,CityArray);
sp_dropdown.setAdapter(stringArrayAdapter);
sp_dropdown.setSelection(0);
sp_dropdown.setOnItemSelectedListener(this);
}
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
ToastUtil.show(this,"你选择的是"+CityArray[i]);
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
}
因为数组适配器只能显示一行文本,因此可以使用简单适配器,同时放置图片和文本,可以使用简单适配器
package com.example.highcontrolleractivity;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.SimpleAdapter;
import android.widget.Spinner;
import com.example.highcontrolleractivity.util.ToastUtil;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SpinnerIconActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener {
private static final int[] iconArray = {
R.drawable.shuixing, R.drawable.jinxing, R.drawable.diqiu,
R.drawable.huoxing, R.drawable.muxing, R.drawable.tuxing
};
private static final String[] starArray = {"水星", "金星", "地球", "火星", "木星", "土星"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_spinner_icon);
Spinner sp_icon=findViewById(R.id.sp_icon);
List<Map<String,Object>> list=new ArrayList<>();
for (int i = 0; i < iconArray.length; i++) {
Map<String,Object> stringObjectMap=new HashMap<>();
stringObjectMap.put("icon",iconArray[i]);
stringObjectMap.put("name",starArray[i]);
list.add(stringObjectMap);
}
SimpleAdapter simpleAdapter=new SimpleAdapter(this,list,
R.layout.item_simple,
new String[]{"icon","name"},
new int[]{R.id.iv_icon,R.id.tv_name});
sp_icon.setAdapter(simpleAdapter);
sp_icon.setSelection(0);
sp_icon.setOnItemSelectedListener(this);
}
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
ToastUtil.show(this, "您选择的是" + starArray[i]);
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
}
假如现在需要设置的下拉列表为,左边是图片,右边是星球名字,右下角要附上简述,就得使用BaseAdapter这种更上层的Adapter适配器,BaseAdapter是一个抽象方法,
主要通过重写getView()方法设置下拉列表的视图信息,因此BaseAdapter拥有更高的适应性。
例子:
PlaneBaseAdapter类:
package com.example.highcontrolleractivity.adapter;
import android.annotation.SuppressLint;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.example.highcontrolleractivity.R;
import com.example.highcontrolleractivity.entity.Planet;
import java.util.List;
public class PlaneBaseAdapter extends BaseAdapter {
private Context context;
private List<Planet> planetList;
public PlaneBaseAdapter(Context context, List<Planet> planetList) {
this.context = context;
this.planetList = planetList;
}
@Override
public int getCount() {
return planetList.size();
}
@Override
public Object getItem(int i) {
return planetList.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
@SuppressLint("ViewHolder") View view1= LayoutInflater.from(context).inflate(R.layout.item_list,null);
ImageView imageView=view1.findViewById(R.id.iv_icon);
TextView tv_name=view1.findViewById(R.id.tv_name);
TextView tv_desc=view1.findViewById(R.id.tv_desc);
Planet planet=planetList.get(i);
imageView.setImageResource(planet.image);
tv_name.setText(planet.name);
tv_desc.setText(planet.desc);
return view1;
}
}
Activity类:
package com.example.highcontrolleractivity;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Spinner;
import com.example.highcontrolleractivity.adapter.PlaneBaseAdapter;
import com.example.highcontrolleractivity.entity.Planet;
import com.example.highcontrolleractivity.util.ToastUtil;
import java.util.List;
public class BaseAdapterAcivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener {
private List<Planet> planetList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_base_adapter_acivity);
Spinner sp_planet = findViewById(R.id.sp_baseAdapter);
planetList = Planet.getDefaultList();
// 构建一个行星列表的适配器
PlaneBaseAdapter adapter = new PlaneBaseAdapter(this, planetList);
sp_planet.setAdapter(adapter);
sp_planet.setSelection(0);
sp_planet.setOnItemSelectedListener(this);
}
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
ToastUtil.show(this, "您选择的是" + planetList.get(position).name);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
}
plane类:
package com.example.highcontrolleractivity.entity;
import com.example.highcontrolleractivity.R;
import java.util.ArrayList;
import java.util.List;
public class Planet {
public int image; // 行星图标
public String name; // 行星名称
public String desc; // 行星描述
public Planet(int image, String name, String desc) {
this.image = image;
this.name = name;
this.desc = desc;
}
private static int[] iconArray = {R.drawable.shuixing, R.drawable.jinxing, R.drawable.diqiu,
R.drawable.huoxing, R.drawable.muxing, R.drawable.tuxing};
private static String[] nameArray = {"水星", "金星", "地球", "火星", "木星", "土星"};
private static String[] descArray = {
"水星是太阳系八大行星最内侧也是最小的一颗行星,也是离太阳最近的行星",
"金星是太阳系八大行星之一,排行第二,距离太阳0.725天文单位",
"地球是太阳系八大行星之一,排行第三,也是太阳系中直径、质量和密度最大的类地行星,距离太阳1.5亿公里",
"火星是太阳系八大行星之一,排行第四,属于类地行星,直径约为地球的53%",
"木星是太阳系八大行星中体积最大、自转最快的行星,排行第五。它的质量为太阳的千分之一,但为太阳系中其它七大行星质量总和的2.5倍",
"土星为太阳系八大行星之一,排行第六,体积仅次于木星"
};
public static List<Planet> getDefaultList() {
List<Planet> planetList = new ArrayList<Planet>();
for (int i = 0; i < iconArray.length; i++) {
planetList.add(new Planet(iconArray[i], nameArray[i], descArray[i]));
}
return planetList;
}
}
即使是下拉列表,在屏幕中展示的条数也是有限的,因此需要复用convertview使得屏幕能够通过拉动的形式展示更多的条数。
对上面做出优化:
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
//当一个屏幕显示的条数构建完,再需要构建就会走else那部分,复用并刷新数据
ViewHolder viewHolder;
if(view==null) {
view = LayoutInflater.from(context).inflate(R.layout.item_list, null);
viewHolder= new ViewHolder();
viewHolder.iv_icon = view.findViewById(R.id.iv_icon);
viewHolder.tv_name = view.findViewById(R.id.tv_name);
viewHolder.tv_desc = view.findViewById(R.id.tv_desc);
view.setTag(viewHolder);
}
else {
viewHolder=(ViewHolder) view.getTag();
}
Planet planet=planetList.get(i);
viewHolder.iv_icon.setImageResource(planet.image);
viewHolder.tv_name.setText(planet.name);
viewHolder.tv_desc.setText(planet.desc);
return view;
}
public static final class ViewHolder{
public ImageView iv_icon;
public TextView tv_name;
public TextView tv_desc;
}
ListView是一个显示滚动项列表的示视图组(viewgroup),通过使用适配器(Adapter)把这些列表项自动插入到列表中。适配器比如从一个数组或是数据库查询获取到数据,然后转换每一项成为可放入到列表的视图,一般也是联合BaseAdapter一起使用,要注意的一点是当条目事件里面存在Button等点击事件的时候,会出现条目事件冲突,因为条目点击事件会交给子容器处理,因此不生效,要在子条目视图中使用descendantFocusability=“blocksDescendants”>阻止下级控件的获取,解决冲突问题。
例子:设置两个checkbox,分别设置是否显示列表视图的分割线和按压背景,按压背景默认是灰色,可自定义。
package com.example.highcontrolleractivity;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ListView;
import com.example.highcontrolleractivity.adapter.PlaneBaseAdapter;
import com.example.highcontrolleractivity.entity.Planet;
import com.example.highcontrolleractivity.util.ToastUtil;
import com.example.highcontrolleractivity.util.Utils;
import java.util.List;
public class ListViewActivity extends AppCompatActivity implements AdapterView.OnItemClickListener, CompoundButton.OnCheckedChangeListener {
private ListView iv_planet;
private List<Planet> planetList;
@SuppressLint("MissingInflatedId")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_view);
iv_planet=findViewById(R.id.iv_planet);
planetList=Planet.getDefaultList();
PlaneBaseAdapter planeBaseAdapter=new PlaneBaseAdapter(this,planetList);
iv_planet.setAdapter(planeBaseAdapter);
iv_planet.setOnItemClickListener(this);
CheckBox ck_divider=findViewById(R.id.ck_divider);
CheckBox ck_selector=findViewById(R.id.ck_selector);
ck_divider.setOnCheckedChangeListener(this);
ck_selector.setOnCheckedChangeListener(this);
}
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
ToastUtil.show(this,"您选择的星球是"+planetList.get(i).name);
}
@SuppressLint("NonConstantResourceId")
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
switch (compoundButton.getId()){
case R.id.ck_divider:
if(compoundButton.isChecked()){
@SuppressLint("UseCompatLoadingForDrawables") Drawable divider=getResources().getDrawable(R.color.black,getTheme());
iv_planet.setDivider(divider);
iv_planet.setDividerHeight(Utils.dip2px(this,1));
}
else{
iv_planet.setDivider(null);
iv_planet.setDividerHeight(0);
}
break;
case R.id.ck_selector:
if(compoundButton.isChecked()){
@SuppressLint("UseCompatLoadingForDrawables") Drawable selector=getResources().getDrawable(R.drawable.list_selector,getTheme());
iv_planet.setSelector(selector);
}
else{
@SuppressLint("UseCompatLoadingForDrawables") Drawable selector=getResources().getDrawable(R.color.transparent,getTheme());
iv_planet.setSelector(selector);
}
break;
}
}
}
GirdView的属性:
XML中的属性 | GirdView类中的设置方法 | 说明 |
---|---|---|
horizontalSpace | setHorizontalSpacing | 指定网格项在水平方向的间距 |
verticalSpacing | setVerticalSpacing | 指定网格项在垂直方向的间距 |
numColumns | setNumColumns | 指定列数目 |
stretchMode | setStretchMode | 指定剩余空间的拉伸模式 |
columnWidth | setColumnWidth | 指定每列宽度 |
关于stretchMode的设置:
XML中的属性 | GirdView类中的设置方法 | 说明 |
---|---|---|
none | NO_STRETCH | 不拉伸 |
columnWidth | STRETCH_COLUMN_WIDTH | 若有剩余空间,拉伸列宽挤掉空隙 |
spacingWidth | STRETCH_SPACING | 若有剩余空间,则列宽不变,把空间分配到每列间的空隙 |
spacingWidthUniform | STRETCH_SPACING_UNIFORM | 若有剩余空间,列宽不变,把空间分配到列作业间隙 |
例子:还是星球的例子
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".GirdViewActivity"
android:orientation="vertical">
<GridView
android:id="@+id/gv_planet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:numColumns="2"
android:verticalSpacing="3dp"
android:horizontalSpacing="3dp"
android:columnWidth="100dp"
android:stretchMode="columnWidth"/>
LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:padding="5dp">
<TextView
android:id="@+id/tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="地球"
android:textSize="17sp"
android:textColor="@color/black"
android:gravity="center"/>
<ImageView
android:id="@+id/iv_icon"
android:layout_width="match_parent"
android:layout_height="100dp"
android:scaleType="fitCenter"
tools:src="@drawable/diqiu"/>
<TextView
android:id="@+id/tv_desc"
android:layout_width="match_parent"
android:layout_height="95dp"
android:textSize="13sp"
android:textColor="@color/black"
android:gravity="start|top"
tools:text="木星是太阳系八大行星中体积最大、自转最快的行星,排行第五。它的质量为太阳的千分之一,但为太阳系中其它七大行星质量总和的2.5倍。"/>
LinearLayout>
package com.example.highcontrolleractivity;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.GridView;
import com.example.highcontrolleractivity.adapter.PlaneBaseWithButtonAdapter;
import com.example.highcontrolleractivity.entity.Planet;
import com.example.highcontrolleractivity.util.ToastUtil;
import java.util.List;
public class GirdViewActivity extends AppCompatActivity implements AdapterView.OnItemClickListener {
private GridView gridView;
private List<Planet> planetList;
@SuppressLint("MissingInflatedId")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_gird_view);
gridView = findViewById(R.id.gv_planet);
planetList = Planet.getDefaultList();
PlaneBaseWithButtonAdapter planeBaseWithButtonAdapter=new PlaneBaseWithButtonAdapter(this, planetList);
gridView.setAdapter(planeBaseWithButtonAdapter);
gridView.setOnItemClickListener(this);
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
ToastUtil.show(this, "您选择了:" + planetList.get(position).name);
}
}
同样,需要从PagetAdapter类中继承,然后重写类的方法。
例子:对星球的视图进行翻页处理,并在上方启用标签栏。
Adapater类:
package com.example.highcontrolleractivity.adapter;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
import com.example.highcontrolleractivity.ViewPagerActivity;
import com.example.highcontrolleractivity.entity.Planet;
import java.util.ArrayList;
import java.util.List;
public class ImagePagerAdapter extends PagerAdapter {
private final ViewPagerActivity context;
private final List<Planet> planetList;
private final List<ImageView> ViewList=new ArrayList<>();
public ImagePagerAdapter(ViewPagerActivity context, List<Planet> planetList) {
this.context=context;
this.planetList=planetList;
for (Planet planet : planetList) {
ImageView view=new ImageView(context);
view.setImageResource(planet.image);
view.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
ViewList.add(view);
}
}
@Override
public int getCount() {
return ViewList.size();
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return view==object;
}
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
//添加view到container,后返回一个与view关联的对象,这里直接返回自身,与isViewFromObject方法中的object相等
ImageView view=ViewList.get(position);
container.addView(view);
return view;
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView(ViewList.get(position));
}
//返回文字到标签栏
@Nullable
@Override
public CharSequence getPageTitle(int position) {
return planetList.get(position).name;
}
}
Activity类:
package com.example.highcontrolleractivity;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.PagerTabStrip;
import androidx.viewpager.widget.ViewPager;
import android.graphics.Color;
import android.os.Bundle;
import android.util.TypedValue;
import com.example.highcontrolleractivity.adapter.ImagePagerAdapter;
import com.example.highcontrolleractivity.entity.Planet;
import com.example.highcontrolleractivity.util.ToastUtil;
import java.util.ArrayList;
import java.util.List;
public class ViewPagerActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener {
private ViewPager vp_content;
private List<Planet> planetList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_pager);
initPagerStrip();
initViewPager();
}
//初始化翻页视图
private void initViewPager() {
vp_content = findViewById(R.id.vp_content);
planetList = Planet.getDefaultList();
PagerAdapter pagerAdapter= new ImagePagerAdapter(this, planetList);
vp_content.setAdapter(pagerAdapter);
vp_content.addOnPageChangeListener(this);
vp_content.setCurrentItem(2);
}
//初始化翻页标签栏
private void initPagerStrip() {
PagerTabStrip pagerTabStrip=findViewById(R.id.pts_tab);
//此包源于androidx,相当于android的扩展,因此能够直接使用sp,不需要自己转换
pagerTabStrip.setTextSize(TypedValue.COMPLEX_UNIT_SP,20);
pagerTabStrip.setTextColor(Color.BLACK);
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
ToastUtil.show(this, "您翻到的手机品牌是:" + planetList.get(position).name);
}
@Override
public void onPageScrollStateChanged(int state) {
}
}
一般进入程序后会现出现引导页,引导页面进入,原理其实就是通过Viewpager翻页的形式制作引导页,通过PageAdapter嵌入view视图。
引导页的视图:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/iv_launch"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"/>
<RadioGroup
android:id="@+id/rg_indicate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:paddingBottom="20dp"
android:orientation="horizontal" />
<Button
android:id="@+id/btn_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="22sp"
android:textColor="@color/black"
android:text="立即开启美好生活"
android:visibility="gone"
android:layout_centerInParent="true"/>
RelativeLayout>
Activity视图:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".SimpleLaunchActivity">
<androidx.viewpager.widget.ViewPager
android:id="@+id/vp_launch"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
LinearLayout>
Adapter类:
package com.example.highcontrolleractivity.adapter;
import android.os.Build;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import androidx.annotation.NonNull;
import androidx.viewpager.widget.PagerAdapter;
import com.example.highcontrolleractivity.R;
import com.example.highcontrolleractivity.SimpleLaunchActivity;
import com.example.highcontrolleractivity.util.ToastUtil;
import java.util.ArrayList;
import java.util.List;
public class SimpleLaunchAdapter extends PagerAdapter {
private final List<View> viewList=new ArrayList<>();
public SimpleLaunchAdapter(SimpleLaunchActivity context, int[] arrayPic) {
for (int i = 0; i < arrayPic.length; i++) {
View view= LayoutInflater.from(context).inflate(R.layout.item_launch,null);
ImageView iv_launch=view.findViewById(R.id.iv_launch);
RadioGroup rg_indicate=view.findViewById(R.id.rg_indicate);
Button btn_start=view.findViewById(R.id.btn_start);
iv_launch.setImageResource(arrayPic[i]);
//每个页面的RadioGroup要有四个RadioButton
for (int j = 0; j < arrayPic.length; j++) {
RadioButton radioButton=new RadioButton(context);
radioButton.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
radioButton.setPadding(10,10,10,10);
rg_indicate.addView(radioButton);
}
//当前位置的单选按钮要高亮显示
((RadioButton)rg_indicate.getChildAt(i)).setChecked(true);
//最后一个引导页则开启开关
if(i==arrayPic.length-1){
btn_start.setVisibility(View.VISIBLE);
btn_start.setOnClickListener(v->{
ToastUtil.show(context,"开启美好生活,跳入应用程序主页面");
});
}
viewList.add(view);
}
}
@Override
public int getCount() {
return viewList.size();
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return view==object;
}
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
View view=viewList.get(position);
container.addView(view);
return view;
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView(viewList.get(position));
}
}
Activity类:
package com.example.highcontrolleractivity;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager.widget.ViewPager;
import android.annotation.SuppressLint;
import android.os.Bundle;
import com.example.highcontrolleractivity.adapter.SimpleLaunchAdapter;
public class SimpleLaunchActivity extends AppCompatActivity {
private final int[] ArrayPic=new int[]{
R.drawable.guide_bg1, R.drawable.guide_bg2,
R.drawable.guide_bg3, R.drawable.guide_bg4
};
@SuppressLint({"MissingInflatedId", "LocalSuppress"})
private ViewPager vp_launch;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_simple_launch);
vp_launch = findViewById(R.id.vp_launch);
SimpleLaunchAdapter launchAdapter=new SimpleLaunchAdapter(this,ArrayPic);
vp_launch.setAdapter(launchAdapter);
}
}