[mobilesafe] 11_自定义Toast的风格-归属地显示的风格

Android 4.0

自定义Toast的风格-归属地显示的风格-查看金山卫士

1、首先自定义组合控件-用来选择归属地提示框的风格
2、然后在SettingActivity中应用该组件控件,用来弹出一个对话框来选择风格
3、然后保存选择的风格到SharedPreferences
4、在ShowAddressService中根据保存在SharedPreferences中的值,来加载不同的提示框样式
核心代码:
a) toast_view_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- 19、自定义的Toast提示框-组合控件布局文件 -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="65dp"
    android:background="@drawable/toast_hint_dialog_status_selector" >
    <TextView
        android:id="@+id/tv_toast_setting_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="5dip"
        android:layout_marginTop="8dip"
        android:textColor="#ff000000"
        android:textSize="18sp" />
    <TextView
        android:id="@+id/tv_toast_setting_description"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/tv_toast_setting_title"
        android:layout_marginLeft="5dip"
        android:layout_marginTop="5dip"
        android:textColor="#99000000"
        android:textSize="14sp" />
    <ImageView
        android:src="@drawable/toast_arraws_disable"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:layout_marginRight="5dip"
        android:clickable="false"
        android:contentDescription="@string/tost_arraws_hint"
        android:focusable="false" />
    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:layout_alignParentBottom="true"
        android:layout_marginLeft="5dip"
        android:layout_marginRight="5dip"
        android:background="#000000" />
</RelativeLayout>
b) 
ToastView.java
package cn.zengfansheng.mobilesafe.ui;
 
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.TextView;
import cn.zengfansheng.mobilesafe.R;
 
/**
* 3、自定义的组合控件,用于自定义土司提示框的布局
* @author hacket
*/

public class ToastView extends RelativeLayout {
 
    private TextView tv_setting_title;// 标题
    private TextView tv_setting_desc;// 描述
 
    //private String desc_on;
 
    public ToastView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initViewFromXml(context);
        System.out.println("SettingView三个参数的构造方法。。。");
    }
 
    public ToastView(Context context, AttributeSet attrs) {
        super(context, attrs);
 
        initViewFromXml(context);
 
        TypedArray typedArray = context.obtainStyledAttributes(attrs,
                R.styleable.ToastSettingView);
        // 把自定义的属性和attrs的集合建立一个对应关系
        String title = typedArray.getString(R.styleable.ToastSettingView_toast_title);
        String desc = typedArray.getString(R.styleable.ToastSettingView_toast_desc);
        setTitle(title);// 设置标题
        setDesc(desc);
        //desc_on = typedArray.getString(R.styleable.ToastSettingView_toast_desc_on);
        typedArray.recycle();
    }
 
    public ToastView(Context context) {
        super(context);
        initViewFromXml(context);
        System.out.println("SettingView一个参数的构造方法。。。");
    }
 
    /**
     * 1、初始化自定义布局文件,将xml布局文件转换成view对象
     *
     * @param context
     */

    public void initViewFromXml(Context context) {
        // 1、 inflate一个xml文件转换成View对象,this表示挂载在什么下面(也就是认谁当爹)
        View view = View.inflate(context, R.layout.toast_view_layout, this);
 
        // 2、初始化组件
        tv_setting_title = (TextView) view.findViewById(R.id.tv_toast_setting_title);
        tv_setting_desc = (TextView) view.findViewById(R.id.tv_toast_setting_description);
    }
 
    /**
     * 2、获取标题的文本
     * @return 获取到的标题
     */

    public String getTitle() {
        return tv_setting_title.getText().toString().trim();
    }
    /**
     * 3、获取到的描述信息
     * @return 获取到的描述信息
     */

    public String getDesc() {
        return tv_setting_desc.getText().toString().trim();
    }
 
    /**
     * 4、设置自定义组合控件的标题
     *
     * @param title 要设置的标题
     */

    public void setTitle(String title) {
        tv_setting_title.setText(title);
    }
 
    /**
     * 5、设置自定义组合控件的描述
     * @param desc 要设置的描述信息
     */

    public void setDesc(String desc) {
        tv_setting_desc.setText(desc);
    }
}
c) attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="ToastSettingView">  <!-- name:Toast自定义样式名称 -->
        <attr name="toast_title" format="string" />  <!-- 自定义属性  -->
        <attr name="toast_desc" format="string" />
    </declare-styleable>
</resources>
d) SettingActivity.java
package cn.zengfansheng.mobilesafe;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import cn.zengfansheng.mobilesafe.service.ShowAddressService;
import cn.zengfansheng.mobilesafe.ui.SettingView;
import cn.zengfansheng.mobilesafe.ui.ToastView;
import cn.zengfansheng.mobilesafe.utils.ServiceStatusUtils;

/**
 * 3、SettingActivity 程序设置界面
 * @author hacket
 * 
 */

public class SettingActivity extends Activity {
    private static final String TAG = "SettingActivity";
    private SettingView sv_setting_autoUpdate;// 自动更新
    private SharedPreferences sp;
    private SettingView sv_setting_showPhoneAddress;// 来电归属地查询
    private Intent addressIntent;// 显示来电归属地Service

    private ToastView toast_setting_hint_phone_address;// 修改该来电归属地提示框样式
    private final String[] items = { "半透明", "活力橙", "卫士蓝", "苹果绿", "金属灰" };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_setting);

        sv_setting_autoUpdate = (SettingView) this.findViewById(R.id.sv_setting_autoUpdate);

        // 2、获取一个SharedPreferences实例
        sp = this.getSharedPreferences("config", MODE_PRIVATE);
        boolean isAutoUpdate = sp.getBoolean("autoUpdate", true);// 默认为自动更新
        if (isAutoUpdate) {
            sv_setting_autoUpdate.setChecked(true);
        } else {
            sv_setting_autoUpdate.setChecked(false);
        }

        // 1、注册自动更新点击事件
        sv_setting_autoUpdate.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Editor edit = sp.edit();
                if (sv_setting_autoUpdate.isChecked()) {// 1-1开启了自动更新,那么再点击会关闭自动更新
                    sv_setting_autoUpdate.setChecked(false);
                    edit.putBoolean("autoUpdate", false);
                } else {// 1-2自动更新关闭了,那么再次点击开启自动更新
                    sv_setting_autoUpdate.setChecked(true);
                    edit.putBoolean("autoUpdate", true);
                }
                edit.commit();
            }
        });

        // 2、注册来电提醒是否提醒事件
        sv_setting_showPhoneAddress = (SettingView) this.findViewById(R.id.sv_setting_show_phone_address);
        // 检测一下, 是否服务运行,不能写在OnCreate()方法中,按Home后台,此时,停止该服务,再进来,服务状态的√不会去掉。因为OnCreate()只有被创建的时候才会被执行。
        /*boolean isServiceOpen = ServiceStatusUtils.isServiceOpen(this,
                ShowAddressService.class);
        if (isServiceOpen) {// 服务开启了
            Log.i(TAG, "手机归属地查询服务开启了。。。");
            sv_setting_showPhoneAddress.setChecked(true);
        } else {
            sv_setting_showPhoneAddress.setChecked(false);
            Log.i(TAG, "手机归属地查询服务关闭了。。。");
        }*/

        sv_setting_showPhoneAddress.setOnClickListener(new OnClickListener() {
           
            @Override
            public void onClick(View v) {
                if (sv_setting_showPhoneAddress.isChecked()) {
                    sv_setting_showPhoneAddress.setChecked(false);
                    stopService(addressIntent);// 停止服务
                    Log.i(TAG, "手机归属地查询服务关闭了。。。");
                }else {
                    sv_setting_showPhoneAddress.setChecked(true);
                    addressIntent = new Intent(SettingActivity.this, ShowAddressService.class);
                    startService(addressIntent);// 开启服务
                    Log.i(TAG, "手机归属地查询服务开启了。。。");
                }
            }
        });

        // 4、显示更改来电归属地提示框背景
        toast_setting_hint_phone_address = (ToastView) this.findViewById(R.id.toast_setting_hint_phone_address);
        int toastWhich = sp.getInt("toastWhich", 0);
        toast_setting_hint_phone_address.setDesc(items[toastWhich]);
        toast_setting_hint_phone_address.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                showChangeDialog();
            }
        });
    }


    /**
     *5、改变toast来电提醒提示框的背景 
     */

    private void showChangeDialog() {
        AlertDialog.Builder builder = new Builder(this);// 只能用this
        builder.setTitle("选择归属地提示风格");
        int toastWhich = sp.getInt("toastWhich", 0);
        builder.setSingleChoiceItems(items, toastWhich,new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                //记住哪个样式
                Log.i(TAG, "您选择了"+items[which]);
                Editor edit = sp.edit();
                edit.putInt("toastWhich",which);
                edit.commit();
                dialog.dismiss();
           
                toast_setting_hint_phone_address.setDesc(items[which]);   
            }
        });
        builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {
            }
        });
        builder.show();
    }


    // 3、检测一下, 是否服务运行,用户界面可见时判断服务是否开启
    @Override
    protected void onStart() {
        super.onStart();
        boolean isServiceOpen = ServiceStatusUtils.isServiceOpen(this,ShowAddressService.class);
        if (isServiceOpen) {// 服务开启了
            Log.i(TAG, "手机归属地查询服务开启了。。。");
            sv_setting_showPhoneAddress.setChecked(true);
        } else {
            sv_setting_showPhoneAddress.setChecked(false);
            Log.i(TAG, "手机归属地查询服务关闭了。。。");
        }
    }
}
e)ShowAddressService.java
package cn.zengfansheng.mobilesafe.service;

import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.graphics.PixelFormat;
import android.os.IBinder;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
import cn.zengfansheng.mobilesafe.R;
import cn.zengfansheng.mobilesafe.dao.AddressDao;

/**
 * 2、显示来电归属地Service
 * @author hacket
 */

public class ShowAddressService extends Service {

    private MyPhoneStateListener listener;// 电话状态监听器
    private TelephonyManager manager;// 电话管理器
    private OutCallReveiver outCallReveiver;// 外拨电话的广播接收者

    private WindowManager windowManager;// window管理者-自定义Toast弹出内容
    //private TextView tv_toast;//土司textview
    private View view_toast;// 使用xml自定义toast的布局

   
    // private final String[] items = { "半透明", "活力橙", "卫士蓝", "苹果绿", "金属灰" };
    private final int[] bgs = { R.drawable.call_locate_white,   //提示框背景颜色
            R.drawable.call_locate_orange, R.drawable.call_locate_blue,
            R.drawable.call_locate_green, R.drawable.call_locate_gray };

    private SharedPreferences sp;


    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    /**
     * 3、弹出自定义的土司
     * @param phoneAddress 要弹出的土司
     */

    public void toastAddress(String phoneAddress) {
       
        //a) 得到窗体管理者
        windowManager = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);

        // b) 设置要显示的view
        /*tv_toast = new TextView(getApplicationContext());
        tv_toast.setText(phoneAddress);
        tv_toast.setTextSize(20);
        tv_toast.setTextColor(Color.RED);*/


        view_toast = View.inflate(getApplicationContext(), R.layout.toast_info,null);
        int toastWhich = sp.getInt("toastWhich", 0);//选择的是哪一个边框样式
        view_toast.setBackgroundResource(bgs[toastWhich]);
       
        TextView tv_toast_info = (TextView) view_toast.findViewById(R.id.tv_toast_info);
        tv_toast_info.setText(phoneAddress);

        final WindowManager.LayoutParams params = new WindowManager.LayoutParams();;
        params.height = WindowManager.LayoutParams.WRAP_CONTENT;// 高度包裹内容
        params.width = WindowManager.LayoutParams.WRAP_CONTENT;// 宽度包裹内容
        params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE// 标记-不可获取焦点
                | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE// 不可触摸
                | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;// 保持屏幕常亮度
        params.format = PixelFormat.TRANSLUCENT;// 透明
        params.type = WindowManager.LayoutParams.TYPE_TOAST;// 类型
        windowManager.addView(view_toast, params);
    }


    // 广播接受者的有效周期 与 服务的生命周期关联.
    // 广播接受者 不仅可以通过清单文件注册 还可以通过 代码注册.
    /**
     * 2、查询号码归属地内部类的外拨电话的广播接收者
     * @author hacket
     */

    private class OutCallReveiver extends BroadcastReceiver {

        private static final String TAG = "Inner OutCallReveiver";

        @Override
        public void onReceive(Context context, Intent intent) {
            Log.i(TAG, "我是ShowAddressService中的OutCallReveiver,我被开启了。。。");
            String phoneAddress = AddressDao.getAddress(context, getResultData());
            // Toast.makeText(context, phoneAddress, Toast.LENGTH_LONG).show();
            toastAddress(phoneAddress);
        }
    }

    @Override
    public void onCreate() {

        // 2、服务开启,注册外拨电话的广播接收者
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_NEW_OUTGOING_CALL);
        outCallReveiver = new OutCallReveiver();
        // 在服务被创建的时候,注册广播接收者
        registerReceiver(outCallReveiver, filter);

        // 1、服务创建,开始电话状态监听
        manager = (TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE);

        // PhoneStateListener listener = new MyPhoneStateListener();
        // 注意,定义成了成员变量,就不能再这样定义了,否则该服务已开启就没有关闭,当卸载服务时,卸载的是外部定义的服务,
        // 这个没有卸载,就一直这样存活着,当有来电的时候,还是会显示来电归属地
        listener = new MyPhoneStateListener();// 这就正常了。
        manager.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);

        sp = this.getSharedPreferences("config", MODE_PRIVATE);
        super.onCreate();
    }

    @Override
    public void onDestroy() {
       
        // 2、服务停止时,卸载掉外拨电话的广播接收者,防止报异常。
        unregisterReceiver(outCallReveiver);
        outCallReveiver = null;
       
        // 1、当服务停止的时候 取消电话监听
        manager.listen(listener, PhoneStateListener.LISTEN_NONE);
        listener = null;
        super.onDestroy();
    }

    /**
     * 1、电话状态监听器
     * @author hacket
     */

    private class MyPhoneStateListener extends PhoneStateListener {

        @Override
        public void onCallStateChanged(int state, String incomingNumber) {
            super.onCallStateChanged(state, incomingNumber);

            switch (state) {

            case TelephonyManager.CALL_STATE_RINGING:// 电话响铃时
                String phoneAddress = AddressDao.getAddress(getApplicationContext(), incomingNumber);
                // Toast.makeText(getApplicationContext(), phoneAddress,Toast.LENGTH_LONG).show();
                toastAddress(phoneAddress);
                break;

            case TelephonyManager.CALL_STATE_IDLE:// 电话空闲,也就是电话挂断了,或者没接通
                if (windowManager != null) {
                    windowManager.removeView(view_toast);// 在电话空闲的时候,将土司显示到窗体的内容给移除掉
                    view_toast = null;
                }
                break;
            }
        }
    }
}
结果: