安卓基础学习(续)

28 分钟

十一、综合训练(购物车功能)

实现功能:

手机商品页面展示,加入购物车功能,商品详情页面,清空购物车,删除购物车商品。
  1. 数据库准备,准备的数据库有商品数据库,还有存储购物车信息的数据库,使用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;
    }
    @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, "成功添加至购物车");
    }
}

商品展示视图:

<?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"/>

    <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>

购物车标志视图:

<?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">

    <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>

file

file

file

file

十二、内容提供者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;
    }

    @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;
        }

    }
}
  1. 运行时动态申请权限

    Android系统未了防止某些App滥用权限,从6.0开始引入运行时权限管理机制,允许App在允许过程中拥有某项权限,一旦缺少,可自动弹出小窗口提示开启权限,
    也可在应用启动时候即申请全部权限,一般流程如下:
  2. 检查App是否开启指定权限,调用ContextCompat的checkSelfPermission方法
  3. 请求系统弹窗,便于用户选择,调用ActivityCompat的requestPermissions方法,命令系统弹出申请窗口
  4. 重写获得页面的权限请求回调方法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;
     @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;
    }

}
  1. 添加查询联系人

    利用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);

    }
}
  1. 监听短信内容

    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();
        }
    }
}
  1. 发送彩信

    通过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,"请在弹窗中选择短信或者信息应用");
    
     }
    }

    file

  2. 通过MediaStore查询图片

    需求如下,发送彩信,从手机中查询6张合适的图片显示到屏幕中供用户选择,用户点击图片即可发送彩信,需通过FileProvider,将文件暴露给第三方,并授予读写权限。
<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path name="external_storage_download" path="Download"/>

</paths>
<?xml version="1.0" encoding="utf-8"?>
<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>
        <!--安卓7.0,把访问文件的Uri改成了FileProvider-->
        <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,"请在弹窗中选择短信或者信息应用");

    }
}

file

  1. 应用安装

    要使用应用安装也要通过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,它的继承结构如下:

file

1. 下拉列表Spinner

下拉列表有两种模式,分别为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) {

    }
}

2.简单适配器

因为数组适配器只能显示一行文本,因此可以使用简单适配器,同时放置图片和文本,可以使用简单适配器
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) {

    }
}

file

3. BaseAdapter

假如现在需要设置的下拉列表为,左边是图片,右边是星球名字,右下角要附上简述,就得使用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;
        }
    }

file

  1. ConvertView复用

    即使是下拉列表,在屏幕中展示的条数也是有限的,因此需要复用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;
    }

4.列表视图listView

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;
        }
    }
}

file

5.网格视图GirdView

GirdView的属性:

XML中的属性GirdView类中的设置方法说明
horizontalSpacesetHorizontalSpacing指定网格项在水平方向的间距
verticalSpacingsetVerticalSpacing指定网格项在垂直方向的间距
numColumnssetNumColumns指定列数目
stretchModesetStretchMode指定剩余空间的拉伸模式
columnWidthsetColumnWidth指定每列宽度

关于stretchMode的设置:

XML中的属性GirdView类中的设置方法说明
noneNO_STRETCH不拉伸
columnWidthSTRETCH_COLUMN_WIDTH若有剩余空间,拉伸列宽挤掉空隙
spacingWidthSTRETCH_SPACING若有剩余空间,则列宽不变,把空间分配到每列间的空隙
spacingWidthUniformSTRETCH_SPACING_UNIFORM若有剩余空间,列宽不变,把空间分配到列作业间隙

例子:还是星球的例子

<?xml version="1.0" encoding="utf-8"?>
<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>
<?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"
    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);
    }
}

6.翻页视图ViewPager

同样,需要从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) {

    }

}

file

7.ViewPager设置引导页

一般进入程序后会现出现引导页,引导页面进入,原理其实就是通过Viewpager翻页的形式制作引导页,通过PageAdapter嵌入view视图。

引导页的视图:

<?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="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视图:

<?xml version="1.0" encoding="utf-8"?>
<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);

    }
}

file
file

8.碎片Fragment

传统的Activity不能很好的处理大屏问题,急需碎片化的东西能够划区域展示内容,且有属于自己的独立可操作空间,因此诞生了Fragment,Fragment注册方式分为动态注册和静态注册:
静态注册:直接在布局文件中直接指定Fragment,动态注册:直接在代码中动态添加Fragment。
Fragment生命周期:onAttach->onCreate->OnCreateView->onViewCreate->onStart->onResume->onPause->onStop->onDestroyView->onDestory->onDetach
  1. 静态注册
    当加载layout的fragment页面时,会自动加载指向的fragment类,进而加载fragment页面
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <fragment
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:id="@+id/fragment_static"
        android:name="com.example.highcontrolleractivity.fragment.StaticFragment"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="17sp"
        android:textColor="@color/black"
        android:gravity="center"
        android:text="fragment展示页"/>
</LinearLayout>

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#ddffdd"
    android:padding="5dp"
    android:orientation="horizontal">

    <TextView
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:text="广告图片"
        android:textColor="@color/black"
        android:textSize="17sp"/>
    <ImageView
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="4"
        android:src="@drawable/adv"
        android:scaleType="fitCenter"/>

</LinearLayout>

file

  1. 动态注册

    同样需要从FragmentAdapter继承类,重写此类承载ViewPager,与之前有所不同的是getItem需要返回的是Fragment类,所以需要重写Fragment方法,
    利用onCreateView创建视图效果。

例子:之前星球的例子

主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.DynamicFragmentAdapter;
import com.example.highcontrolleractivity.entity.Planet;

import java.util.List;

public class FragmentDynamicActivity extends AppCompatActivity {
    private ViewPager vp_content;
    private List<Planet> planetList;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragment_dynamic);
        initPagerStrip();
        initViewPager();
    }

    private void initViewPager() {
        vp_content = findViewById(R.id.vp_content);
        planetList = Planet.getDefaultList();
        DynamicFragmentAdapter dynamicFragmentAdapter=new DynamicFragmentAdapter(getSupportFragmentManager(),planetList);
        vp_content.setAdapter(dynamicFragmentAdapter);
        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);
    }
}

FragmentAdapter类:

package com.example.highcontrolleractivity.adapter;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;

import com.example.highcontrolleractivity.entity.Planet;
import com.example.highcontrolleractivity.fragment.DynamicFragment;
import java.util.List;

public class DynamicFragmentAdapter extends FragmentPagerAdapter {
    private List<Planet> planetList;
    public DynamicFragmentAdapter(@NonNull FragmentManager fm, List<Planet> planetList) {
        //将当前的fragment设置为resume状态,上一个fragment设置为start
        //从而通过fragment的onResume()来懒加载数据
        super(fm,BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
        this.planetList=planetList;
    }

    @NonNull
    @Override
    public Fragment getItem(int position) {
        Planet planet=planetList.get(position);
        return DynamicFragment.newInstance(position,planet.image,planet.desc);
    }

    @Override
    public int getCount() {
        return planetList.size();
    }

    @Nullable
    @Override
    public CharSequence getPageTitle(int position) {
        return planetList.get(position).name;
    }
}

DynamicFragment类:

package com.example.highcontrolleractivity.adapter;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;

import com.example.highcontrolleractivity.entity.Planet;
import com.example.highcontrolleractivity.fragment.DynamicFragment;
import java.util.List;

public class DynamicFragmentAdapter extends FragmentPagerAdapter {
    private List<Planet> planetList;
    public DynamicFragmentAdapter(@NonNull FragmentManager fm, List<Planet> planetList) {
        //将当前的fragment设置为resume状态,上一个fragment设置为start
        //从而通过fragment的onResume()来懒加载数据
        super(fm,BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
        this.planetList=planetList;
    }

    @NonNull
    @Override
    public Fragment getItem(int position) {
        Planet planet=planetList.get(position);
        return DynamicFragment.newInstance(position,planet.image,planet.desc);
    }

    @Override
    public int getCount() {
        return planetList.size();
    }

    @Nullable
    @Override
    public CharSequence getPageTitle(int position) {
        return planetList.get(position).name;
    }
}

主Activity的xml与之前Viewpager相同,Fragment的xml与之前地球子视图相同。

file

  1. 使用Fragment提高引导页的性能

    之前的引导页,事实上就是将全部页面都存到了内存之中,每切换一个页面则从内存中提取,一旦页面很多,性能就十分不好,
    通过Fragment搭配ViewPager,一旦发生页面切换,则相邻页面会被加载,非相邻页面会被回收,提高了启动速度,也不会造成资源的浪费。

主要Fragment代码如下,其它代码与上方例子相似:

package com.example.highcontrolleractivity.fragment;

import android.content.Context;
import android.os.Bundle;

import androidx.fragment.app.Fragment;

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 com.example.highcontrolleractivity.R;
import com.example.highcontrolleractivity.util.ToastUtil;


public class LaunchFragment extends Fragment {


    public static LaunchFragment newInstance(int count,int position,int image) {
        LaunchFragment fragment = new LaunchFragment();
        Bundle args = new Bundle();
        args.putInt("count",count);
        args.putInt("position",position);
        args.putInt("image",image);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
       Bundle bundle=getArguments();
       Context context=getContext();
        assert bundle != null;
        int count=bundle.getInt("count");
        int image=bundle.getInt("image");
        int position=bundle.getInt("position");
            View view= LayoutInflater.from(context).inflate(R.layout.fragment_launch,container,false);
            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(image);
            //每个页面的RadioGroup要有四个RadioButton
            for (int j = 0; j < count; 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(position)).setChecked(true);

            //最后一个引导页则开启开关
            if(position==count-1){
                btn_start.setVisibility(View.VISIBLE);
                btn_start.setOnClickListener(v->{
                    ToastUtil.show(context,"开启美好生活,跳入应用程序主页面");
                });
            }
        return view;
    }
}

9.综合训练(记账本功能)

实现的功能:
  1. 记账功能,能够选择日期,选择收入或支出,填入事项说明,金额插入数据库
  2. 查询功能,能够根据根据,展示出当月的所有支出和收入
    数据库设计:
    账单表:id,date(日期),type(支出、收入),amount(金额),remark(说明)
    功能实现:
    通过OpenDBHelper初始化数据库,并通过页面设置添加进入数据库,查询功能使用ViewPager+ListView实现,通过ViewPager+Fragment的形式
    承载ListView进行实现。

实现如下:

添加账单bill_add页面:

<?xml version="1.0" encoding="utf-8"?>
<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"
    android:orientation="vertical"
    tools:context=".BillPagerActivity">
    <include layout="@layout/title_booking"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center|top"
        android:orientation="vertical"
        android:paddingLeft="5dp"
        android:paddingRight="5dp">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:orientation="horizontal">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:gravity="center|right"
                android:text="请选择月份:"
                android:textColor="@color/black"
                android:textSize="17sp" />

            <TextView
                android:id="@+id/tv_month"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="2"
                android:drawableRight="@drawable/arrow_down"
                android:gravity="center"
                android:textColor="@color/black"
                android:textSize="17sp"
                tools:text="2035-05"/>
        </LinearLayout>

        <androidx.viewpager.widget.ViewPager
            android:id="@+id/vp_bill"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <androidx.viewpager.widget.PagerTabStrip
                android:id="@+id/pts_bill"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
        </androidx.viewpager.widget.ViewPager>
    </LinearLayout>
</LinearLayout>

bill_pager页面:

<?xml version="1.0" encoding="utf-8"?>
<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"
    android:orientation="vertical"
    tools:context=".BillPagerActivity">
    <include layout="@layout/title_booking"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center|top"
        android:orientation="vertical"
        android:paddingLeft="5dp"
        android:paddingRight="5dp">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:orientation="horizontal">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:gravity="center|right"
                android:text="请选择月份:"
                android:textColor="@color/black"
                android:textSize="17sp" />

            <TextView
                android:id="@+id/tv_month"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="2"
                android:drawableRight="@drawable/arrow_down"
                android:gravity="center"
                android:textColor="@color/black"
                android:textSize="17sp"
                tools:text="2035-05"/>
        </LinearLayout>

        <androidx.viewpager.widget.ViewPager
            android:id="@+id/vp_bill"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <androidx.viewpager.widget.PagerTabStrip
                android:id="@+id/pts_bill"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
        </androidx.viewpager.widget.ViewPager>
    </LinearLayout>
</LinearLayout>

item_bill页面:

<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:orientation="horizontal">

    <TextView
        android:id="@+id/tv_date"
        android:layout_width="0dp"
        android:layout_height="45dp"
        android:layout_weight="1"
        android:gravity="center"
        android:textColor="@color/black"
        android:textSize="16sp"
        tools:text="2035-09" />

    <TextView
        android:id="@+id/tv_remark"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="2"
        android:gravity="center"
        android:textColor="@color/black"
        android:textSize="16sp"
        tools:text="foods" />

    <TextView
        android:id="@+id/tv_amount"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:gravity="center"
        android:textColor="@color/black"
        android:textSize="16sp"
        tools:text="200" />

</LinearLayout>

fragment_bill页面:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center|top"
    android:orientation="vertical">

    <ListView
        android:id="@+id/lv_bill"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

title_booking页面:

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:background="#aaaaff">

    <ImageView
        android:id="@+id/iv_back"
        android:layout_width="50dp"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"
        android:padding="5dp"
        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:textSize="20sp"
        android:textColor="@color/black"
        android:layout_centerInParent="true"
        android:gravity="center"
        tools:text="账单列表"/>
    
    <TextView
        android:id="@+id/tv_option"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:textSize="17sp"
        android:textColor="@color/black"
        android:layout_alignParentEnd="true"
        android:layout_marginEnd="15dp"
        android:gravity="center"
        tools:text="添加账单"/>
</RelativeLayout>

BillInfo实体类:

package com.example.highcontrolleractivity.entity;

public class BillInfo {
    public int id;
    public String date;
    public int type;
    public double amount;
    public String remark;

    // 账单类型,0 收入,1 支出
    public static final int BILL_TYPE_INCOME = 0;
    public static final int BILL_TYPE_COST = 1;

    @Override
    public String toString() {
        return "BillInfo{" +
                "id=" + id +
                ", date='" + date + '\'' +
                ", type=" + type +
                ", amount=" + amount +
                ", remark='" + remark + '\'' +
                '}';
    }
}

BillDBHelper类:

package com.example.highcontrolleractivity.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 androidx.annotation.Nullable;

import com.example.highcontrolleractivity.entity.BillInfo;

import java.util.ArrayList;
import java.util.List;

public class BillDBHelper extends SQLiteOpenHelper {
    private static final String DB_NAME="bill.db";
    private static final int DB_VERSION=1;
    private static BillDBHelper dbHelper=null;
    private static final String TABLE_BILLS_INFO = "bill_info";
    private  SQLiteDatabase mRDB=null;
    private SQLiteDatabase mWDB=null;
    public BillDBHelper(Context context) {
        super(context,DB_NAME,null,DB_VERSION);
    }

    public static  BillDBHelper getInstance(Context context){
        if(dbHelper==null){
            dbHelper=new BillDBHelper(context);
        }
        return dbHelper;
    }

    public SQLiteDatabase openReadLink(){
        if(mRDB==null || !mRDB.isOpen()){
            mRDB=dbHelper.getReadableDatabase();
        }
        return mRDB;
    }
    public SQLiteDatabase openWriteLink() {
        if (mWDB == null || !mWDB.isOpen()) {
            mWDB = dbHelper.getWritableDatabase();
        }
        return mWDB;
    }
    public void closeLink() {
        if (mRDB != null && mRDB.isOpen()) {
            mRDB.close();
            mRDB = null;
        }

        if (mWDB != null && mWDB.isOpen()) {
            mWDB.close();
            mWDB = null;
        }
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        String sql = "CREATE TABLE IF NOT EXISTS " + TABLE_BILLS_INFO +
                "(_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," +
                " date VARCHAR NOT NULL," +
                " type INTEGER NOT NULL," +
                " amount DOUBLE NOT NULL," +
                " remark VARCHAR NOT NULL);";
        sqLiteDatabase.execSQL(sql);

    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {

    }
    public long save(BillInfo billInfo){
        ContentValues values=new ContentValues();
        values.put("date",billInfo.date);
        values.put("type",billInfo.type);
        values.put("amount",billInfo.amount);
        values.put("remark",billInfo.remark);
        return mRDB.insert(TABLE_BILLS_INFO,null,values);
    }
    @SuppressLint("Range")
    public List<BillInfo> queryByMonth(String yearMonth){
        List<BillInfo> list=new ArrayList<>();
        String sql = "select * from " + TABLE_BILLS_INFO + " where date like '" + yearMonth + "%'";
        @SuppressLint("Recycle") Cursor cursor=mRDB.rawQuery(sql,null);
        while (cursor.moveToNext()) {
            BillInfo bill = new BillInfo();
            bill.id = cursor.getInt(cursor.getColumnIndex("_id"));
            bill.date = cursor.getString(cursor.getColumnIndex("date"));
            bill.type = cursor.getInt(cursor.getColumnIndex("type"));
            bill.amount = cursor.getDouble(cursor.getColumnIndex("amount"));
            bill.remark = cursor.getString(cursor.getColumnIndex("remark"));
            list.add(bill);
        }
        return  list;
    }
}

BillAddActivity类:

package com.example.highcontrolleractivity;

import static com.example.highcontrolleractivity.R.id.btn_save;
import static com.example.highcontrolleractivity.R.id.et_remark;
import static com.example.highcontrolleractivity.R.id.iv_back;
import static com.example.highcontrolleractivity.R.id.rg_type;

import androidx.appcompat.app.AppCompatActivity;

import android.annotation.SuppressLint;
import android.app.DatePickerDialog;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.RadioGroup;
import android.widget.TextView;

import com.example.highcontrolleractivity.database.BillDBHelper;
import com.example.highcontrolleractivity.entity.BillInfo;
import com.example.highcontrolleractivity.util.DateUtil;
import com.example.highcontrolleractivity.util.ToastUtil;

import java.util.Calendar;

public class BillAddActivity extends AppCompatActivity implements View.OnClickListener, DatePickerDialog.OnDateSetListener {

    private Calendar calendar;
    private TextView tv_date;
    private BillDBHelper billDBHelper;
    private RadioGroup rg_type;
    private EditText et_remark;
    private EditText et_amount;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bill_add);
        TextView tv_title = findViewById(R.id.tv_title);
        TextView tv_option=findViewById(R.id.tv_option);
        tv_date = findViewById(R.id.tv_date);
        rg_type = findViewById(R.id.rg_type);
        et_remark = findViewById(R.id.et_remark);
        et_amount = findViewById(R.id.et_amount);
        findViewById(btn_save).setOnClickListener(this);
        tv_title.setText("添加账单");
        tv_option.setText("账单列表");
        tv_option.setOnClickListener(this);
        findViewById(iv_back).setOnClickListener(this);
        tv_date.setOnClickListener(this);
        calendar = Calendar.getInstance();
        String date = DateUtil.getDate(calendar);
        tv_date.setText(date);

        billDBHelper = BillDBHelper.getInstance(this);
        billDBHelper.openReadLink();
        billDBHelper.openWriteLink();
    }

    @SuppressLint("NonConstantResourceId")
    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.tv_date:
                DatePickerDialog datePickerDialog=new DatePickerDialog(this,this,
                        calendar.get(Calendar.YEAR),
                        calendar.get(Calendar.MONTH),
                        calendar.get(Calendar.DAY_OF_MONTH));
                datePickerDialog.show();
                break;
            case R.id.btn_save:
                BillInfo billInfo=new BillInfo();
                billInfo.date=tv_date.getText().toString();
                billInfo.type=rg_type.getCheckedRadioButtonId()==R.id.rb_income ? BillInfo.BILL_TYPE_INCOME : BillInfo.BILL_TYPE_COST;
                billInfo.remark=et_remark.getText().toString();
                billInfo.amount= Double.parseDouble(et_amount.getText().toString());
                if(billDBHelper.save(billInfo)>0){
                    ToastUtil.show(this,"保存账单成功");
                }
                break;
            case R.id.tv_option:
                // 跳转到添加账单页面
                Intent intent = new Intent(this, BillPagerActivity.class);
                intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                startActivity(intent);
                break;

            case R.id.iv_back:
                // 关闭当前页面
                finish();
                break;

        }

    }

    @Override
    public void onDateSet(DatePicker datePicker, int year, int month, int day) {
        calendar.set(Calendar.YEAR,year);
        calendar.set(Calendar.MONTH,month);
        calendar.set(Calendar.DAY_OF_MONTH,day);
        tv_date.setText(DateUtil.getDate(calendar));
    }

}

BillPager类:

package com.example.highcontrolleractivity;

import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager.widget.PagerTabStrip;
import androidx.viewpager.widget.ViewPager;

import android.annotation.SuppressLint;
import android.app.DatePickerDialog;
import android.content.Intent;
import android.os.Bundle;
import android.util.TypedValue;
import android.view.View;
import android.widget.DatePicker;
import android.widget.TextView;

import com.example.highcontrolleractivity.adapter.BillPagerAdapter;
import com.example.highcontrolleractivity.database.BillDBHelper;
import com.example.highcontrolleractivity.util.DateUtil;

import java.util.Calendar;

public class BillPagerActivity extends AppCompatActivity implements View.OnClickListener, DatePickerDialog.OnDateSetListener {

    private Calendar calendar;
    private TextView tv_month;
    private ViewPager vp_bill;
    private BillDBHelper mDBHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bill_pager);
        TextView tv_title=findViewById(R.id.tv_title);
        TextView tv_option=findViewById(R.id.tv_option);
        tv_month = findViewById(R.id.tv_month);
        tv_title.setText("账单列表");
        tv_option.setText("添加账单");
        tv_option.setOnClickListener(this);
        tv_month.setOnClickListener(this);
        calendar = Calendar.getInstance();
        tv_month.setText(DateUtil.getMonth(calendar));
        findViewById(R.id.iv_back).setOnClickListener(this);
        mDBHelper = BillDBHelper.getInstance(this);
        mDBHelper.openReadLink();
        mDBHelper.openWriteLink();
        initViewPager();

    }

    private void initViewPager() {
        PagerTabStrip pts_bill=findViewById(R.id.pts_bill);
        pts_bill.setTextSize(TypedValue.COMPLEX_UNIT_SP, 17);
        vp_bill=findViewById(R.id.vp_bill);
        BillPagerAdapter adapter=new BillPagerAdapter(getSupportFragmentManager(), calendar.get(Calendar.YEAR));
        vp_bill.setAdapter(adapter);
        vp_bill.setCurrentItem(calendar.get(Calendar.MONTH));

    }

    @SuppressLint("NonConstantResourceId")
    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.tv_month:
                DatePickerDialog datePickerDialog=new DatePickerDialog(this,this,
                        calendar.get(Calendar.YEAR),
                        calendar.get(Calendar.MONTH),
                        calendar.get(Calendar.DAY_OF_MONTH));
                datePickerDialog.show();
                break;
            case R.id.tv_option:
                // 跳转到添加账单页面
                Intent intent = new Intent(this, BillAddActivity.class);
                intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                startActivity(intent);
                break;

            case R.id.iv_back:
                // 关闭当前页面
                finish();
                break;
        }
    }

    @Override
    public void onDateSet(DatePicker datePicker, int year, int month, int day) {
        calendar.set(Calendar.YEAR,year);
        calendar.set(Calendar.MONTH,month);
        calendar.set(Calendar.DAY_OF_MONTH,day);
        tv_month.setText(DateUtil.getMonth(calendar));
        vp_bill.setCurrentItem(month);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mDBHelper.closeLink();
    }
}

BillPagerAdapter类:

package com.example.highcontrolleractivity.adapter;



import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;

import com.example.highcontrolleractivity.fragment.BillFragment;

public class BillPagerAdapter extends FragmentPagerAdapter {
    private final  int mYear;
    public BillPagerAdapter(@NonNull FragmentManager fm,int year) {
        super(fm,BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
        this.mYear = year;
    }

    @NonNull
    @Override
    public Fragment getItem(int position) {
        int month=position+1;
        String zeroMonth = month < 10 ? "0" + month : String.valueOf(month);
        String yearMonth = mYear + "-" + zeroMonth;
        return BillFragment.newInstance(yearMonth);
    }

    @Override
    public int getCount() {
        return 12;
    }

    @Nullable
    @Override
    public CharSequence getPageTitle(int position) {
        return (position+1)+"月份";
    }
}

BillFragment类:

package com.example.highcontrolleractivity.fragment;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;

import androidx.fragment.app.Fragment;
import com.example.highcontrolleractivity.R;
import com.example.highcontrolleractivity.adapter.BillListAdapter;
import com.example.highcontrolleractivity.database.BillDBHelper;
import com.example.highcontrolleractivity.entity.BillInfo;

import java.util.List;

public class BillFragment extends Fragment {
    public static BillFragment newInstance(String yearMonth){
        BillFragment fragment = new BillFragment();
        Bundle args = new Bundle();
        args.putString("yearMonth", yearMonth);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                              Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_bill, container, false);
        ListView lv_bill=view.findViewById(R.id.lv_bill);
        BillDBHelper mDBHelper = BillDBHelper.getInstance(getContext());
        Bundle arguments = getArguments();
        String yearMonth = arguments.getString("yearMonth");
        List<BillInfo> billInfoList = mDBHelper.queryByMonth(yearMonth);
        BillListAdapter adapter = new BillListAdapter(getContext(), billInfoList);
        lv_bill.setAdapter(adapter);
        return view;
    }
}

BillListAdapter类:

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.TextView;

import com.example.highcontrolleractivity.R;
import com.example.highcontrolleractivity.entity.BillInfo;

import java.util.List;


public class BillListAdapter extends BaseAdapter {


    private final Context mContext;
    private final List<BillInfo> mBillList;

    public BillListAdapter(Context context, List<BillInfo> billInfoList) {
        this.mContext = context;
        this.mBillList = billInfoList;
    }

    @Override
    public int getCount() {
        return mBillList.size();
    }

    @Override
    public Object getItem(int position) {
        return mBillList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return mBillList.get(position).id;
    }

    @SuppressLint("DefaultLocale")
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        if (convertView == null) {
            holder = new ViewHolder();
            convertView = LayoutInflater.from(mContext).inflate(R.layout.item_bill, null);
            holder.tv_date = convertView.findViewById(R.id.tv_date);
            holder.tv_remark = convertView.findViewById(R.id.tv_remark);
            holder.tv_amount = convertView.findViewById(R.id.tv_amount);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        BillInfo bill = mBillList.get(position);
        holder.tv_date.setText(bill.date);
        holder.tv_remark.setText(bill.remark);
        holder.tv_amount.setText(String.format("%s%d元", bill.type == 0 ? "+" : "-", (int) bill.amount));
        return convertView;
    }
    public final  class ViewHolder{
        public TextView tv_date;
        public TextView tv_remark;
        public TextView tv_amount;
    }

}

实现效果如下:

file

file

十五、广播

标准广播(无序广播)

收发过程分为三个步骤:
1.发送标准广播
2.定义广播接收器
3.开关广播接收器
package com.example.broadcast;

import androidx.appcompat.app.AppCompatActivity;

import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;

import com.example.broadcast.receiver.StandardReceiver;

public class BroadStandardActivity extends AppCompatActivity implements View.OnClickListener {

    private StandardReceiver standardReceiver;

    @SuppressLint("MissingInflatedId")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_broad_standard);
        findViewById(R.id.bt_boardcast).setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        //发送标准广播
        Intent intent=new Intent(StandardReceiver.STANDARD_ACTION);
        sendBroadcast(intent);
    }

    @Override
    protected void onStart() {
        super.onStart();
        standardReceiver = new StandardReceiver();
        //意图过滤器
        IntentFilter filter=new IntentFilter(StandardReceiver.STANDARD_ACTION);
        //注册接收器,注册后正常接收
        registerReceiver(standardReceiver,filter);
    }

    @Override
    protected void onStop() {
        super.onStop();
        //注册接收器,注销后不再接收广播
        unregisterReceiver(standardReceiver);
    }
}

package com.example.broadcast.receiver;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class StandardReceiver extends BroadcastReceiver {
    public static  final String STANDARD_ACTION="com.example.broadcast.standard";
    @Override
    public void onReceive(Context context, Intent intent) {
        if(intent!=null && intent.getAction().equals(STANDARD_ACTION)){
            Log.d("广播:","收到一条广播");
        }
    }
}

有序广播

多个接收器接收广播的先后顺序规则:
  1. 优先级越大的接收器,越早收到有序广播
  2. 优先级相同的时候,越早注册的接收器越早收到有序广播
    当然,先接收到的接收者,即可让其它接收器继续接收,也可中断广播不让其它接收者接收
package com.example.broadcast;

import androidx.appcompat.app.AppCompatActivity;

import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;

import com.example.broadcast.receiver.OrderAReceiver;
import com.example.broadcast.receiver.OrderBReceiver;

public class BroadOrderActivity extends AppCompatActivity implements View.OnClickListener {

    public final  static  String ORDER_ACTION="com.example.broadcast.order";
    private OrderAReceiver orderAReceiver;
    private OrderBReceiver orderBReceiver;

    @SuppressLint("MissingInflatedId")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_broad_order);
        findViewById(R.id.btn_orderboardcast).setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        Intent intent=new Intent(ORDER_ACTION);
        sendOrderedBroadcast(intent,null);
    }

    @Override
    protected void onStart() {
        super.onStart();
        orderAReceiver = new OrderAReceiver();
        IntentFilter Afilter=new IntentFilter(ORDER_ACTION);
        Afilter.setPriority(11); //设置优先级
        registerReceiver(orderAReceiver,Afilter);
        orderBReceiver = new OrderBReceiver();
        IntentFilter Bfilter=new IntentFilter(ORDER_ACTION);
        Bfilter.setPriority(10);
        registerReceiver(orderBReceiver,Bfilter);
    }

    @Override
    protected void onStop() {
        super.onStop();
        unregisterReceiver(orderAReceiver);
        unregisterReceiver(orderBReceiver);
    }
}
package com.example.broadcast.receiver;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

import com.example.broadcast.BroadOrderActivity;

public class OrderAReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if(intent!=null&intent.getAction().equals(BroadOrderActivity.ORDER_ACTION)){
            abortBroadcast();
            Log.d("信息:","A接收到广播");
        }
    }
}

package com.example.broadcast.receiver;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

import com.example.broadcast.BroadOrderActivity;

public class OrderBReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if(intent!=null&intent.getAction().equals(BroadOrderActivity.ORDER_ACTION)){
            Log.d("信息:","B接收到广播");
        }
    }
}

设置了优先级,A的优先级比B的优先级高,所以A可以中断广播,结果显示B接收不到广播。

广播静态注册

静态注册与代码中直接注册差异不大,,同样需要继承BroadcastReceiver并且写onReceiver方法,但
        <receiver
            android:name=".receiver.ShockReceiver"
            android:enabled="true"
            android:exported="true"></receiver>

package com.example.broadcast.receiver;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Vibrator;
import android.util.Log;

public class ShockReceiver extends BroadcastReceiver {
    public  static  final  String SHOCK_ACTION="com.example.broadcat.shock";
    @Override
    public void onReceive(Context context, Intent intent) {
        if(intent!=null&&intent.getAction().equals(SHOCK_ACTION)){
            Log.d("广播","震动");
            //获取震动服务
            Vibrator vb= (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
            //震动多少ms
            vb.vibrate(500);
        }
    }
}

package com.example.broadcast;

import androidx.appcompat.app.AppCompatActivity;

import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;

import com.example.broadcast.receiver.ShockReceiver;

public class BroadStaticActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_broad_static);
        findViewById(R.id.btn_send_shock).setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        String fullName="com.example.broadcast.receiver.ShockReceiver";
        Intent intent=new Intent(ShockReceiver.SHOCK_ACTION);
        ComponentName componentName=new ComponentName(this,fullName);
        intent.setComponent(componentName);
        sendBroadcast(intent);

    }
}

网络变更广播

主要通过意图过滤器android.net.conn.CONNECTIVITY_CHANGE来对网络变更服务进行广播。

制式类型类:

package com.example.broadcast.util;

import android.telephony.TelephonyManager;

public class NetworkUtil {
    // 获取数据连接的制式类型
    public static String getNetworkClass(int subType) {
        switch (subType) {
            case TelephonyManager.NETWORK_TYPE_GPRS:
            case TelephonyManager.NETWORK_TYPE_EDGE:
            case TelephonyManager.NETWORK_TYPE_CDMA:
            case TelephonyManager.NETWORK_TYPE_1xRTT:
            case TelephonyManager.NETWORK_TYPE_IDEN:
                return "2G";
            case TelephonyManager.NETWORK_TYPE_UMTS:
            case TelephonyManager.NETWORK_TYPE_EVDO_0:
            case TelephonyManager.NETWORK_TYPE_EVDO_A:
            case TelephonyManager.NETWORK_TYPE_HSDPA:
            case TelephonyManager.NETWORK_TYPE_HSUPA:
            case TelephonyManager.NETWORK_TYPE_HSPA:
            case TelephonyManager.NETWORK_TYPE_EVDO_B:
            case TelephonyManager.NETWORK_TYPE_EHRPD:
            case TelephonyManager.NETWORK_TYPE_HSPAP:
                return "3G";
            case TelephonyManager.NETWORK_TYPE_LTE:
                return "4G";
            case TelephonyManager.NETWORK_TYPE_NR:
                return "5G";
            default:
                return "未知";
        }
    }
}

接收者类:

package com.example.broadcast.receiver;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.NetworkInfo;
import android.util.Log;

import com.example.broadcast.util.NetworkUtil;

public class SystemnetworkReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if(intent!=null){
            NetworkInfo networkInfo=intent.getParcelableExtra("networkInfo");
            String text = String.format("收到一个网络变更广播,网络大类为%s," +
                            "网络小类为%s,网络制式为%s,网络状态为%s",
                    networkInfo.getTypeName(),
                    networkInfo.getSubtypeName(),
                    NetworkUtil.getNetworkClass(networkInfo.getSubtype()),
                    networkInfo.getState().toString());
            Log.d("网络变更情况", text);
        }
    }
}
package com.example.broadcast;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;

import com.example.broadcast.receiver.SystemnetworkReceiver;

public class SystemnetworkActivity extends AppCompatActivity {

    private SystemnetworkReceiver systemnetworkReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_systemnetwork);
    }
    @Override
    protected void onStart() {
        super.onStart();
        systemnetworkReceiver = new SystemnetworkReceiver();
        IntentFilter filter = new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE");
        registerReceiver(systemnetworkReceiver, filter);
    }
    @Override
    protected void onStop() {
        super.onStop();
        unregisterReceiver(systemnetworkReceiver);
    }

}

定时管理器

AlarmManager定时管理器:
set:设置一次性定时器(安卓6.0之后,在空闲状态下,不保证会发送广播)
setAndAllowWhiledle:设置一次性定时器,即使设备处于空闲状态(暗屏等),也保证执行定时器
setRepeating:设置重复定时器,系统不保证按时发送广播
cancel:取消指定延迟的意图定时器(PendingIntent)

例子:

Activity类:

package com.example.broadcast;

import androidx.appcompat.app.AppCompatActivity;

import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;

import com.example.broadcast.receiver.AlarmReceiver;

public class AlarmActivity extends AppCompatActivity implements View.OnClickListener {

    private AlarmReceiver alarmReceiver;

    @SuppressLint("MissingInflatedId")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_alarm);
        findViewById(R.id.btn_send_alarm).setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        Intent intent=new Intent(alarmReceiver.ALARM_ACTION);
        sendBroadcast(intent);
    }

    @Override
    protected void onStart() {
        super.onStart();
        alarmReceiver = new AlarmReceiver(getApplicationContext());
        IntentFilter intentFilter=new IntentFilter(alarmReceiver.ALARM_ACTION);
        registerReceiver(alarmReceiver,intentFilter);
    }

    @Override
    protected void onStop() {
        super.onStop();
        unregisterReceiver(alarmReceiver);
    }
}

接收者类:

package com.example.broadcast.receiver;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.util.Log;

public class AlarmReceiver extends BroadcastReceiver {

    private final Context mcontext;
    public final String ALARM_ACTION="com.example.broadcast.alarm";
    public AlarmReceiver(Context applicationContext) {
        this.mcontext=applicationContext;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        if(intent!=null&&intent.getAction().equals(ALARM_ACTION)){
            Log.d("信息:","收到一条闹钟广播");
        }
    }

    public void  sendAlarm(){
        Intent intent = new Intent(ALARM_ACTION);
        PendingIntent pendingIntent=PendingIntent.getBroadcast(mcontext,0,intent,PendingIntent.FLAG_IMMUTABLE);
        //从系统服务中获取闹钟管理器
        if(Build.VERSION.SDK_INT>Build.VERSION_CODES.M) {
            // 允许在空闲时发送广播,Android6.0之后新增的方法
            AlarmManager alarmManager = (AlarmManager) mcontext.getSystemService(Context.ALARM_SERVICE);
            alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, 1000, pendingIntent);
        }
        else{
            AlarmManager alarmManager = (AlarmManager) mcontext.getSystemService(Context.ALARM_SERVICE);
            alarmManager.set(AlarmManager.RTC_WAKEUP, 1000, pendingIntent);
        }
    }
}

横屏竖屏切换

横屏竖屏切换会导致Activity的生命周期被打乱,即会销毁先前的Activity,重新开始一个Activity。

例子:

package com.example.broadcast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.annotation.SuppressLint;
import android.content.res.Configuration;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

public class ChangeDirectionActivity extends AppCompatActivity {
    private TextView tv_monitor;
    @SuppressLint("MissingInflatedId")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_change_direction);
        tv_monitor = findViewById(R.id.tv_text);
        Log.d("信息:","create启动");
    }


    @Override
    public void onConfigurationChanged(@NonNull Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        switch (newConfig.orientation){
            case Configuration.ORIENTATION_PORTRAIT:
                tv_monitor.setText("当前屏幕为竖屏方向");
                break;
            case Configuration.ORIENTATION_LANDSCAPE:
                tv_monitor.setText("当前屏幕为横屏方向");
                break;
            default:
                break;
        }

    }
}

在通过自定义配置后,切换横竖屏不会出现Activity重新启动的效果。

画中画

package com.example.broadcast;

import androidx.appcompat.app.AppCompatActivity;

import android.app.PictureInPictureParams;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.util.Rational;

public class ReturnDesktopActivity extends AppCompatActivity {

    private DesktopReceiver desktopReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_return_desktop);
        desktopReceiver = new DesktopReceiver();
        IntentFilter filter=new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
        registerReceiver(desktopReceiver,filter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(desktopReceiver);
    }

    //在进入画中画模式和退出画中画模式触发
    @Override
    public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Configuration newConfig) {
        super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);
        if(isInPictureInPictureMode){
            Log.d("消息","进入画中画模式");
        }
        else{
            Log.d("消息","退出画中画模式");
        }
    }

    private class DesktopReceiver extends BroadcastReceiver{
        @Override
        public void onReceive(Context context, Intent intent) {
            if(intent!=null&&intent.getAction().equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)){
                String reason=intent.getStringExtra("reason");
                if(!TextUtils.isEmpty(reason)&&(reason.equals("homekey"))||reason.equals("recentapps")){
                    //Android8.0后可开启画中画模式
                    if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O &&!isInPictureInPictureMode()){
                        PictureInPictureParams.Builder builder=new PictureInPictureParams.Builder();
                        //下面10/5=2,表示画中画窗口宽度是高度两倍
                        Rational rational=new Rational(10,5);
                        //进入画中画模式
                        builder.setAspectRatio(rational);
                        enterPictureInPictureMode(builder.build());
                    }
                }
            }
        }
    }
}

~  ~  The   End  ~  ~


 赏 
承蒙厚爱,倍感珍贵,我会继续努力哒!
logo图像
tips
文章二维码 分类标签:开发开发
文章标题:安卓基础学习(续)
文章链接:https://aiwin.fun/index.php/archives/2888/
最后编辑:2024 年 1 月 4 日 16:54 By Aiwin
许可协议: 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)
(*) 7 + 9 =
快来做第一个评论的人吧~