首页 > 编程技术 > android

android自定义动态设置Button样式【很常用】

发布时间:2016-9-20 19:57

为了增强android应用的用户体验,我们可以在一些Button按钮上自定义动态的设置一些样式,比如交互时改变字体、颜色、背景图等。

今天来看一个通过重写Button来动态实现一些效果,如圆角矩形、圆形、按下改变字体,改变背景色,改变背景图等

在此说明一下,这种实现方式绝对不是唯一的,而且通过xml文件即可简单实现,这样做只是为了将控件的样式完全由代码实现,更方便打包应用于其他项目

下面来看几张效果图:


 

图1 初始状态 图2 按下第一行的TEXT0


 

图3 按下第二行的TEXT1 图4 按下第三行的TEXT2,按住截屏时,没有截到Toast的提示

下面看代码,共两个类,一个布局文件

1 ButtonM.java:重写Button,可单独打包应用于其他项目

在此说明一下,这种实现方式绝对不是唯一的,而且通过xml文件即可简单实现,这样做只是为了将控件的样式完全由代码实现,更方便打包应用于其他项目


package landptf.control;

import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
/**
 * 重写Button,自定义Button样式
 * @author landptf
 * @date 2015-6-8
 */
public class ButtonM extends Button{
    private GradientDrawable gradientDrawable;//控件的样式
    private String backColors = "";//背景色,String类型
    private int backColori = 0;//背景色,int类型
    private String backColorSelecteds = "";//按下后的背景色,String类型
    private int backColorSelectedi = 0;//按下后的背景色,int类型
    private int backGroundImage = 0;//背景图,只提供了Id
    private int backGroundImageSeleted = 0;//按下后的背景图,只提供了Id
    private String textColors = "";//文字颜色,String类型
    private int textColori = 0;//文字颜色,int类型
    private String textColorSeleteds = "";//按下后的文字颜色,String类型
    private int textColorSeletedi = 0;//按下后的文字颜色,int类型
    private float radius = 8;//圆角半径
    private int shape = 0;//圆角样式,矩形、圆形等,由于矩形的Id为0,默认为矩形
    private Boolean fillet = false;//是否设置圆角

    public ButtonM(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }
    
    public ButtonM(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ButtonM(Context context) {
        this(context, null);
    }
    
    private void init() {
        //将Button的默认背景色改为透明,本人不喜欢原来的颜色
        if (fillet) {
            if (gradientDrawable == null) {
                gradientDrawable = new GradientDrawable();
            }
            gradientDrawable.setColor(Color.TRANSPARENT);
        }else {
            setBackgroundColor(Color.TRANSPARENT);
        }
        //设置文字默认居中
        setGravity(Gravity.CENTER);
        //设置Touch事件
        setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View arg0, MotionEvent event) {
                //按下改变样式
                setColor(event.getAction());
                //此处设置为false,防止Click事件被屏蔽
                return false;
            }
        });
    }
    //改变样式
    private void setColor(int state){
        if (state == MotionEvent.ACTION_DOWN) {
            //按下
            if (backColorSelectedi != 0) {
                //先判断是否设置了按下后的背景色int型
                if (fillet) {
                    if (gradientDrawable == null) {
                        gradientDrawable = new GradientDrawable();
                    }
                    gradientDrawable.setColor(backColorSelectedi);
                }else {
                    setBackgroundColor(backColorSelectedi);
                }
            }else if (!backColorSelecteds.equals("")) {
                if (fillet) {
                    if (gradientDrawable == null) {
                        gradientDrawable = new GradientDrawable();
                    }
                    gradientDrawable.setColor(Color.parseColor(backColorSelecteds));
                }else {
                    setBackgroundColor(Color.parseColor(backColorSelecteds));
                }
            }
            //判断是否设置了按下后文字的颜色
            if (textColorSeletedi != 0) {
                setTextColor(textColorSeletedi);
            }else if (!textColorSeleteds.equals("")) {
                setTextColor(Color.parseColor(textColorSeleteds));
            }
            //判断是否设置了按下后的背景图
            if (backGroundImageSeleted != 0) {
                setBackgroundResource(backGroundImageSeleted);
            }
        }
        if (state == MotionEvent.ACTION_UP) {
            //抬起
            if (backColori == 0 && backColors.equals("")) {
                //如果没有设置背景色,默认改为透明
                if (fillet) {
                    if (gradientDrawable == null) {
                        gradientDrawable = new GradientDrawable();
                    }
                    gradientDrawable.setColor(Color.TRANSPARENT);
                }else {
                    setBackgroundColor(Color.TRANSPARENT);
                }
            }else if(backColori != 0){
                if (fillet) {
                    if (gradientDrawable == null) {
                        gradientDrawable = new GradientDrawable();
                    }
                    gradientDrawable.setColor(backColori);
                }else {
                    setBackgroundColor(backColori);
                }
            }else {
                if (fillet) {
                    if (gradientDrawable == null) {
                        gradientDrawable = new GradientDrawable();
                    }
                    gradientDrawable.setColor(Color.parseColor(backColors));
                }else {
                    setBackgroundColor(Color.parseColor(backColors));
                }
            }
            //如果为设置字体颜色,默认为黑色
            if (textColori == 0 && textColors.equals("")) {
                setTextColor(Color.BLACK);
            }else if (textColori != 0) {
                setTextColor(textColori);
            }else {
                setTextColor(Color.parseColor(textColors));
            }
            if (backGroundImage != 0) {
                setBackgroundResource(backGroundImage);
            }
        }
    }
    
    /**
     * 设置按钮的背景色,如果未设置则默认为透明
     * @param backColor
     */
    public void setBackColor(String backColor) {
        this.backColors = backColor;
        if (backColor.equals("")) {
            if (fillet) {
                if (gradientDrawable == null) {
                    gradientDrawable = new GradientDrawable();
                }
                gradientDrawable.setColor(Color.TRANSPARENT);
            }else {
                setBackgroundColor(Color.TRANSPARENT);
            }
        }else {
            if (fillet) {
                if (gradientDrawable == null) {
                    gradientDrawable = new GradientDrawable();
                }
                gradientDrawable.setColor(Color.parseColor(backColor));
            }else {
                setBackgroundColor(Color.parseColor(backColor));
            }
        }
    }
    
    /**
     * 设置按钮的背景色,如果未设置则默认为透明
     * @param backColor
     */
    public void setBackColor(int backColor) {
        this.backColori = backColor;
        if (backColori == 0) {
            if (fillet) {
                if (gradientDrawable == null) {
                    gradientDrawable = new GradientDrawable();
                }
                gradientDrawable.setColor(Color.TRANSPARENT);
            }else {
                setBackgroundColor(Color.TRANSPARENT);
            }
        }else {
            if (fillet) {
                if (gradientDrawable == null) {
                    gradientDrawable = new GradientDrawable();
                }
                gradientDrawable.setColor(backColor);
            }else {
                setBackgroundColor(backColor);
            }
        }
    }

    /**
     * 设置按钮按下后的颜色
     * @param backColorSelected
     */
    public void setBackColorSelected(int backColorSelected) {
        this.backColorSelectedi = backColorSelected;
    }
    
    /**
     * 设置按钮按下后的颜色
     * @param backColorSelected
     */
    public void setBackColorSelected(String backColorSelected) {
        this.backColorSelecteds = backColorSelected;
    }
    
    

    /**
     * 设置按钮的背景图
     * @param backGroundImage
     */
    public void setBackGroundImage(int backGroundImage) {
        this.backGroundImage = backGroundImage;
        if (backGroundImage != 0) {
            setBackgroundResource(backGroundImage);
        }
    }

    /**
     * 设置按钮按下的背景图
     * @param backGroundImageSeleted
     */
    public void setBackGroundImageSeleted(int backGroundImageSeleted) {
        this.backGroundImageSeleted = backGroundImageSeleted;
    }

    /**
     * 设置按钮圆角半径大小
     * @param radius
     */
    public void setRadius(float radius) {
        if (gradientDrawable == null) {
            gradientDrawable = new GradientDrawable();
        }
        gradientDrawable.setCornerRadius(radius);
    }

    /**
     * 设置按钮文字颜色
     * @param textColor
     */
    public void setTextColors(String textColor) {
        this.textColors = textColor;
        setTextColor(Color.parseColor(textColor));
    }
    
    /**
     * 设置按钮文字颜色
     * @param textColor
     */
    public void setTextColori(int textColor) {
        this.textColori = textColor;
        setTextColor(textColor);
    }
    
    /**
     * 设置按钮按下的文字颜色
     * @param textColor
     */
    public void setTextColorSelected(String textColor) {
        this.textColorSeleteds = textColor;
    }
    
    /**
     * 设置按钮按下的文字颜色
     * @param textColor
     */
    public void setTextColorSelected(int textColor) {
        this.textColorSeletedi = textColor;
    }
    
    /**
     * 按钮的形状
     * @param shape
     */
    public void setShape(int shape) {
        this.shape = shape;
    }

    /**
     * 设置其是否为圆角
     * @param fillet
     */
    @SuppressWarnings("deprecation")
    public void setFillet(Boolean fillet) {
        this.fillet = fillet;
        if (fillet) {
            if (gradientDrawable == null) {
                gradientDrawable = new GradientDrawable();
            }
            //GradientDrawable.RECTANGLE
            gradientDrawable.setShape(shape);
            gradientDrawable.setCornerRadius(radius);
            setBackgroundDrawable(gradientDrawable);
        }
    }
}

2 activity_buttonm.xml 布局文件,为了演示效果定义了三个空的LinearLayout,下面将分别为其添加子控件


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    
    <LinearLayout
        android:id="@+id/ll_button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:orientation="horizontal">
        
    </LinearLayout>
    
    <LinearLayout
        android:id="@+id/ll_button2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:orientation="horizontal">
        
    </LinearLayout>
    
    <LinearLayout
        android:id="@+id/ll_button3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:orientation="horizontal">
        
    </LinearLayout>
    

</LinearLayout>


3 ButtonMActivity.java:ButtonM测试类


package landptf.control;

import android.app.Activity;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.widget.LinearLayout;
import android.widget.Toast;

/**
 * ButtonM测试类
 * @author landptf
 * @date 2015-6-8
 */
public class ButtonMActivity extends Activity{
    
    //定义三个空布局用来装载Button控件,只为演示效果,实际开发中不推荐使用
    private LinearLayout llButtonM1;
    private LinearLayout llButtonM2;
    private LinearLayout llButtonM3;
    //定义三个ButtonM数组
    private ButtonM[] buttonM1;
    private ButtonM[] buttonM2;
    private ButtonM[] buttonM3;
    //定义两组颜色值,按下与未按下的按钮背景色
    private static final String[] colorList = {"#7067E2","#FF618F","#B674D2","#00C2EB"};
    private static final String[] colorSelectedList = {"#3C3779","#88354C","#613E70","#00677D"};
    
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_buttonm);
        initView();
    }
    
    //初始化控件
    private void initView() {
        //实例化布局控件
        llButtonM1 = (LinearLayout) findViewById(R.id.ll_button1);
        llButtonM2 = (LinearLayout) findViewById(R.id.ll_button2);
        llButtonM3 = (LinearLayout) findViewById(R.id.ll_button3);
        //实例化控件数组,各定义4个
        buttonM1 = new ButtonM[4];
        buttonM2 = new ButtonM[4];
        buttonM3 = new ButtonM[4];
        //获取屏幕的宽度,每行四个Button,间隙为60共300,除4为每个控件的宽度
        @SuppressWarnings("deprecation")
        int btnWidth = (getWindowManager().getDefaultDisplay().getWidth() - 300)/4;
        //定义第一个布局
        LinearLayout.LayoutParams lp1;
        for (int i = 0; i < 4; i++) {
            //为buttonM1设置样式,直角矩形
            buttonM1[i] = new ButtonM(this);
            //字体颜色
            buttonM1[i].setTextColori(android.graphics.Color.WHITE);
            //字体大小
            buttonM1[i].setTextSize(14);
            //背景色
            buttonM1[i].setBackColor(Color.parseColor(colorList[i]));
            //选中的背景色
            buttonM1[i].setBackColorSelected(Color.parseColor(colorSelectedList[i]));
            //文字提示
            buttonM1[i].setText("TEXT" + i);
            //此处设置Id的值为i,否则onClick中v.getId()将永远为-1
            buttonM1[i].setId(i);
            //定义buttonM1的布局,宽度自适应,高度为宽度的0.6倍,权重为1
            //也可以写成lp1 = new LinearLayout.LayoutParams(btnWidth,(int) (btnWidth * 0.6));
            lp1 = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,(int) (btnWidth * 0.6), 1.0f);
            //控件距离其右侧控件的距离,此处为60
            lp1.setMargins(0,0,60,0);
            buttonM1[i].setLayoutParams(lp1);
            //设置buttonM1的点击事件
            buttonM1[i].setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(ButtonMActivity.this, "您选择了第" + v.getId() + "个", Toast.LENGTH_SHORT).show();
                }
            });
            //设置PaddingLeft为60
            llButtonM1.setPadding(60, 0, 0, 0);
            //将buttonM1添加到llButtonM1中
            llButtonM1.addView(buttonM1[i]);
        }
        
        //定义第二个布局
        LinearLayout.LayoutParams lp2;
        for (int i = 0; i < 4; i++) {
            //为buttonM2设置样式,圆角矩形
            buttonM2[i] = new ButtonM(this);
            buttonM2[i].setTextColori(android.graphics.Color.WHITE);
            buttonM2[i].setTextSize(14);
            //设置是否为圆角
            buttonM2[i].setFillet(true);
            //设置圆角的半径大小
            buttonM2[i].setRadius(18);
            buttonM2[i].setBackColor(Color.parseColor(colorList[i]));
            buttonM2[i].setBackColorSelected(Color.parseColor(colorSelectedList[i]));
            buttonM2[i].setText("TEXT" + i);
            buttonM2[i].setId(i);
            lp2 = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,(int) (btnWidth * 0.6), 1.0f);
            lp2.setMargins(0,0,60,0);
            buttonM2[i].setLayoutParams(lp2);
            buttonM2[i].setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(ButtonMActivity.this, "您选择了第" + v.getId() + "个", Toast.LENGTH_SHORT).show();
                }
            });
            llButtonM2.setPadding(60, 0, 0, 0);
            llButtonM2.addView(buttonM2[i]);
        }
        //定义第三个布局
        LinearLayout.LayoutParams lp3;
        for (int i = 0; i < 4; i++) {
            //为buttonM3设置样式,圆形
            buttonM3[i] = new ButtonM(this);
            buttonM3[i].setTextColori(android.graphics.Color.WHITE);
            buttonM3[i].setTextSize(14);
            //设置为圆形,默认为矩形,GradientDrawable.RECTANGLE
            buttonM3[i].setShape(GradientDrawable.OVAL);
            buttonM3[i].setFillet(true);
            buttonM3[i].setBackColor(Color.parseColor(colorList[i]));
            buttonM3[i].setBackColorSelected(Color.parseColor(colorSelectedList[i]));
            buttonM3[i].setText("TEXT" + i);
            buttonM3[i].setId(i);
            lp3 = new LinearLayout.LayoutParams(btnWidth,btnWidth);
            lp3.setMargins(0,0,60,0);
            buttonM3[i].setLayoutParams(lp3);
            buttonM3[i].setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(ButtonMActivity.this, "您选择了第" + v.getId() + "个", Toast.LENGTH_SHORT).show();
                }
            });
            llButtonM3.setPadding(60, 0, 0, 0);
            llButtonM3.addView(buttonM3[i]);
        }
    }
}


注释基本都说明了,可以慢慢积累这些控件,最终形成一个自己的控件库,在不同项目中完善,使之越来越强大

明天给大家介绍一下通过继承RelativeLayout,实现多个控件的组合,让不同项目应用,可避免每次都要重写

Android中Last App Switcher应用的一个浮动按钮实现应用的切换非常有用,我现在现在自己尝试仿造一个这样子的功能。

在Android众多工具类app中,Last App Switcher绝对算是一个让人用过就不会卸载的工具。LAS这个应用,它的功能很简单,就是通过一个浮动按钮实现在两个应用之间一键切换,但是非常实用,尤其是在边玩边聊天需要频繁切换应用的时候。所以可以看出,想开发一款受欢迎的应用,一定要注重用户体验,只要用户用的爽,功能再再再简单,它也会受欢迎。那么这功能到底有多简单呢?跟我来实现一下就好了。


我就不截图了,下面用官方的截图来说明。这里真心推荐读者下载用一下。谷歌商店的下载地址:Last App Switcher 搞开发的应该都会FQ吧

看下原始程序界面:

LAS Main View

可以看到主界面就是一系列开关选项,同时程序右边有一个浮动的圆形窗口。下面我会按照步骤一步步增加功能。


仿iOS按钮

写demo不需要多好的界面,但也不能太丑,手里有看起来不错的控件就直接拖进来用了。下面是效果图,这一套按钮有好几种,都是仿iOS的,想要的可以点原作者的这篇博客,源码Github地址。

先添加一个开关主功能的按钮:

初始界面


浮动按钮

可以看到,这个应用的主要功能就在于那个红色的浮动按钮上面。根据程序功能可以知道,这个浮动按钮是由程序开启的服务中创建的。又因为程序的Activity在离开onStart()状态后就会销毁(这样做的原因后面说),之后按钮仍保持其可用状态。所以可以知道是通过startService()启动的服务。下面我们就需要先写一个服务出来,再在服务中绘一个浮动按钮。具体有关服务的细节参考我上一篇博客:博客传送门。

写一个服务FloatButtonService,在AndroidManifest.xml文件添加服务

<service android:name=".FloatButtonService" >
</service>

服务中添加绘制浮动按钮方法,相关说明见注释

private void createFloatView() {

}

方法添加完毕在服务相应的调用位置创建和销毁浮动按钮

@Override
public void onCreate() {
    // TODO Auto-generated method stub
    super.onCreate();
    createFloatView();
}

@Override
public void onDestroy() {
    // TODO Auto-generated method stub
    super.onDestroy();
    if (mFloatLayout != null) {
        mWindowManager.removeView(mFloatLayout);
    }
}

使用浮动按钮还需要增加权限:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

这样,我们在MainActivity中就可以为按钮增加响应事件,进行开启和关闭服务了。

将程序从最近任务(last recent tasks)中移除

  按下系统导航栏第三个按钮我们就可以看到最近使用过的任务列表,当然,LAS切换程序也是在这里选择最后使用的两个应用程序切换的。所以在切换的时候,把自己的Activity从最近的任务中删掉是很必要的。
前面提到过,就是在Activity的onPause()状态或者onStop()状态中执行finishAndRemoveTask()方法删除任务。但这个方法在API 21也就是Android 5.0才引入。不过,我们还有一个更方便的方法,就是在配置文件的<activity>标签中增加

android:excludeFromRecents="true"

这样不论你是按下back键还是home键,程序都会从最近使用过的任务列表中删除

任务间的切换

  将自身Activity从最近任务列表中删除后,我们就可以考虑获取最后两次的任务,然后互相一键切换了。
在浮动按钮的单击事件中添加
首先需要获得ActivityManager的对象

ActivityManager mActivityManager = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);

要获取任务还需要对应权限

<uses-permission android:name="android.permission.GET_TASKS"/>

有了权限,就可以获取到任务列表了

List<ActivityManager.RecentTaskInfo> mAppList = new ArrayList<ActivityManager.RecentTaskInfo>();
mAppList = mActivityManager.getRecentTasks(3, ActivityManager.RECENT_IGNORE_UNAVAILABLE);

建立一个装有RecentTaskInfo的列表,通过getRecentTasks方法获取系统的最近使用过的应用列表。

关于getRecentTasks方法,第一个参数是一个整型值,是你需要返回的应用数量,但实际上得到的数量可能会比这个值要小。比如
我要得到3个,但后台只开了1个,那么只返回1个。第二个参数是要返回的应用的状态,我选择的是忽略不可用的应用,应该是完全关闭,不在后台的应用。

再说一点,这个方法在Android5.0因为安全问题屏蔽掉了,也就是android5.0以上的版本不能用这个方法。所以我前一阵子在App Store上看到评论都是Android5.0用这个没有效果。现在行不行我倒不知道,闲了再研究吧。(每次我说闲了再做基本都是个坑- -|)

前面的参数我之所以要选择3,是因为我只需要获得最近使用的2个应用,因为每次开新应用,这个应用信息都会存在列表的最上面,所以获取前3个即可。

但为什么是3而不是2呢,因为Android的Home界面也是一个Activity(应该是),我可以选择是否要在切换的时候忽略掉Home界面。所以考虑到Home,就要用3。
Home的包名为com.android.launcher,以此为根据进行判断即可。

private void getAndStartIntent(int i){
    ActivityManager.RecentTaskInfo info = mAppList.get(i);
    if (null == info)
        Toast.makeText(FloatButtonService.this, "No other apps", Toast.LENGTH_SHORT).show();
    else if(sp.getBoolean(StringKey.ExcludeHome, false)){ // if set true, do follow func
        if(info.baseIntent.getComponent().getPackageName().equals(HOME_PACKAGE))    //exclude HOME
            getAndStartIntent(2);
    }else
        startActivityWithoutAnimation(info);
}

启动一个应用的过程默认是有一个切换动画的,我们的程序就是用来切换程序的,所以取消启动动画是一个比较好的选择。
只用给要启动的intent加一个flag即可(有些情况下不会生效)

intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);


开机启动

  Android开机启动结束会发送一个BOOT_COMPLETED的广播,我们在程序中建立一个广播接收器来接收这个广播,接收成功就直接启动服务来显示浮动按钮即可。
先建立一个广播接收器 BootReceiver

public class BootReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO Auto-generated method stub
        if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {// on boot
            Intent a = new Intent(context, FloatButtonService.class);
            context.startService(a);
        }
    }
}

在配置文件中,<application>标签下注册广播接收器

<receiver android:name=".BootReceiver" >
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</receiver>

然后增加权限

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

开机启动就完成了。但怎么用开关来控制其是否开机启动呢?

SharedPreferences

  用开关控制功能的开启状态,这个状态不能保存在程序中,因为程序是要被关闭的。那么就是要用一些方法保存开关的状态到系统中,然后服务从文件读取状态,控制自己的程序行为。Android中最适合保存配置状态的就是用SharedPreferences了。当我查看LAS应用的数据文件的时候,发现输出的结果的确是这样的。

cat /data/data/com.abhi.lastappswitcher/shared_prefs/com.inpen.lastAppSwitcher.APPLICATION_PREFS.xml

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<boolean name="com.inpen.lastAppSwitcher.PREF_SNAP_TO_EDGE" value="true" />
<int name="com.inpen.lastAppSwitcher.PREF_LAND_HEIGHT" value="800" />
<int name="com.inpen.lastAppSwitcher.PREF_LAND_FLOATER_Y" value="485" />
<int name="com.inpen.lastAppSwitcher.PREF_LAND_WIDTH" value="1280" />
<int name="com.inpen.lastAppSwitcher.PREF_PORT_FLOATER_Y" value="776" />
<int name="com.inpen.lastAppSwitcher.PREF_PORT_WIDTH" value="800" />
<boolean name="com.inpen.lastAppSwitcher.PREF_ERROR_MSG" value="true" />
<boolean name="com.inpen.lastAppSwitcher.PREF_STATUS_BAR_OVERLAY" value="false" />
<int name="com.inpen.lastAppSwitcher.PREF_FLOATER_SIZE" value="55" />
<int name="com.inpen.lastAppSwitcher.PREF_PORT_FLOATER_X" value="765" />
<int name="com.inpen.lastAppSwitcher.PREF_PORT_HEIGHT" value="1280" />
<int name="com.inpen.lastAppSwitcher.PREF_FLOATER_TRANSPARENCY" value="75" />
<int name="currentQuote" value="6" />
<int name="com.inpen.lastAppSwitcher.PREF_SWITCHING_METHOD" value="1" />
<boolean name="com.inpen.lastAppSwitcher.PREF_FLOATER_MOVABLE" value="true" />
<boolean name="com.inpen.lastAppSwitcher.PREF_HAPTIC_FEEDBACK" value="false" />
<int name="com.inpen.lastAppSwitcher.PREF_FLOATER_COLOR" value="0" />
</map>

那么,我们就可以根据自己的需求来写sharedPreferences文件了
先获得 SharedPreferences 的实例

SharedPreferences sp = getSharedPreferences("las_demo", Context.MODE_PRIVATE);

参数1是不带后缀的文件名,根据文件名获取实例,同一个名字的SharedPreferences对象只获得同一个实例;
参数2是模式操作模式:

    Context.MODE_PRIVATE:
    为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容。
    Context.MODE_APPEND:
    创建的文件是私有数据,该模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件。
    MODE_WORLD_READABLE:
    表示当前文件可以被其他应用读取。
    MODE_WORLD_WRITEABLE:
    表示当前文件可以被其他应用写入。

获得实例之后要进行初始化,写入一些设定值。这里因为初始化只需要一次,但我没找到判断sharedPreferences文件是否存在的方法(没想用File去查,这个文件存在系统路径,有权限问题,估计不行,有知道的可以告诉我),有一个public abstract boolean contains (String key)方法,但用了感觉没效果,所以我又加了一个key,来保存第一次创建的状态,然后写入其他键-值,保存。

if(!sp.getBoolean(StringKey.FirstCreate, true)){

    Editor editor = sp.edit();
    editor.putBoolean(StringKey.FirstCreate, true);
    editor.putBoolean(StringKey.RunAtStart, false);
    editor.putBoolean(StringKey.SnapToEdge, true);
    editor.putBoolean(StringKey.StatusBarOverlay, false);
    editor.putBoolean(StringKey.ExcludeHome, true);

    editor.commit();
}

设置好键-值后就可以根据这些值设置界面里按钮的开关状态和设置程序的一些行为。

if (sp.getBoolean(StringKey.RunAtStart, false))
    mBtnRunAtStartup.setChecked(true);
else
    mBtnRunAtStartup.setChecked(false);

当然,在手动改变按钮状态的时候也要为某个key重新写入新的value

mBtnRunAtStartup.setOnCheckedChangeListener(new OnCheckedChangeListener() {

    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        // TODO Auto-generated method stub
        if (isChecked)
            editBoolKey(StringKey.RunAtStart, true);
        else
            editBoolKey(StringKey.RunAtStart, false);
    }
});


private void editBoolKey(String str, boolean b) {
    Editor editor = sp.edit();
    editor.putBoolean(str, b);
    editor.apply();
}

改SharedPreferences的key-value的时候需要获得editor对象实例,设置完成用apply()方法或者commit()方法提交修改。如果有两个editor实例在同时修改,则以最后一次的提交为准。如果不关心返回值,且在应用的主线程里使用,用apply()要比commit()好。
至此,需要开关功能就在功能实现的地方加一层读取SP键值的过程,根据读到的结果决定功能。是否可用。

悬浮按钮显示在status bar上方

按照下面设置windowManager的属性就好,没什么好解释的,放上文档看吧。

wmParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
               | LayoutParams.FLAG_NOT_FOCUSABLE
               | LayoutParams.FLAG_LAYOUT_IN_SCREEN;

    int android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL = 32 [0x20]
    Window flag: Even when this window is focusable (its is not set), allow any pointer events outside of the window to be sent to the windows behind it. Otherwise it will consume all pointer events itself, regardless of whether they are inside of the window.

    int android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE = 8 [0x8]
    Window flag: this window won't ever get key input focus, so the user can not send key or other button events to it. Those will instead go to whatever focusable window is behind it. This flag will also enable FLAG_NOT_TOUCH_MODAL whether or not that is explicitly set.
    Setting this flag also implies that the window will not need to interact with a soft input method, so it will be Z-ordered and positioned independently of any active input method (typically this means it gets Z-ordered on top of the input method, so it can use the full screen for its content and cover the input method if needed. You can use FLAG_ALT_FOCUSABLE_IM to modify this behavior.

    int android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN = 256 [0x100]
    Window flag: place the window within the entire screen, ignoring decorations around the border (a.k.a. the status bar). The window must correctly position its contents to take the screen decoration into account. This flag is normally set for you by Window as described in Window.setFlags.


按钮边缘吸附效果

  这个应该是最简单的了,在按钮的touch事件中,当移动结束,手指抬起行为ACTION_UP中对位置进行判断,如果按钮的x坐标在屏幕左半边,x设为0,即贴着屏幕左边缘显示,反之一个道理。

在新版的Androiod5.0中,Google定义了Material Design的规范,Android新支持了不少炫酷的动画效果本文我们来介绍一个Activity的切换效果动画,非常炫,应用场景也多。

动画效果图如下:



下面要介绍的其中一种Activity的切换效果(如上图).当前后两个Activity有共同的UI元素时候,适合用这种动画效果,给用户连贯性的体验.

 

实现步骤

1. 新建一个Android应用Project,里面有两个Activity;

2. 下面是MainActivity的代码和XML布局;

   代码和布局都非常简单.解释一下onClick()方法的代码.

   makeSceneTransactionAnimation()方法第二个参数是用来告诉指定共同的UI元素是哪一个,这里是shareView.

   makeSceneTransactionAnimation()方法第三个参数是"robot",和XML布局里面的android:transactionName="robot"的值是一一对应的.


package com.example.garena.myapplication;

import android.app.ActionBar;
import android.app.Activity;
import android.app.ActivityOptions;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final View shareView = findViewById(R.id.share_element_image_view);
        shareView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                ActivityOptions options = ActivityOptions
                        .makeSceneTransitionAnimation(MainActivity.this, shareView, "robot");
                startActivity(intent, options.toBundle());
            }
        });
        ActionBar actionBar = getActionBar();
        if (actionBar != null) {
            actionBar.setTitle(R.string.first_activity);
        }
    }
}



<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="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />

    <ImageView
        android:id="@+id/share_element_image_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_launcher"
        android:transitionName="robot" />
</LinearLayout>



3. 下面是SecondActivity的代码和布局;

    在onClick()方法里面,调用finishAfterTransaction()来finish activity.

    在XML布局里面,同样是需要用android:transactionName="roboto"来标识共同的UI元素.


package com.example.garena.myapplication;

import android.app.ActionBar;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;

public class SecondActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.second_activity);
        ActionBar actionBar = getActionBar();
        if (actionBar !=  null) {
            actionBar.setTitle(R.string.second_activity);
        }
        View btnBack = findViewById(R.id.btn_back);
        btnBack.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finishAfterTransition();
            }
        });
    }
}


<?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:gravity="center"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/title_icon_image_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_launcher"
        android:transitionName="robot" />

    <Button
        android:id="@+id/btn_back"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/back" />
</LinearLayout>



4. 运行效果(如上图);

    从最终运行的效果图可以看到,点击小机器人的图标进入SecondActivity时,能看到小机器人图标移动到SecondActivity这个小机器人图标的位置.

    点击SecondActivity的Back按钮,小机器人图标会移动到MainActivity中小机器人图标的位置.

本教程我们来讲讲两个关于Android开发界面中应用的对话框实例,对于学习Android开发中不知道如何应用Dialog对话框的同学来说是这福利。

用户界面View之Dialog 对话框

一、AlertDialog常用方法
使用AlertDialog.Builder中的create()方法创建一个AlertDialog

setTitle();给对话框设置标题
setIcon();给对话框设置图标
setMessage();设置对话框的提示信息
setView() 给对话框设置自定义样式
setItems();设置对话框要显示的一个list,一般用于显示几个命令时
setSingleChoiceItems();设置对话框显示一个单选的List
setMultiChoiceItems();设置对话框显示一系列的复选框
setNeutralButton();普通按钮
setPositiveButton();给对话框添加"确定"按钮
setNegativeButton();给对话框添加"取消"按钮


示例一:

 

/upload/news/201609/20150601235155772.png



  图1效果:该效果是当按返回按钮时弹出一个提示,来确保无误操作,采用常见的对话框样式。

     代码:

  创建对话框方法dialog()

protected void dialog() {
  AlertDialog.Builder builder = new Builder(Main.this);
  builder.setMessage("确认退出吗?");

  builder.setTitle("提示");

  builder.setPositiveButton("确认", new OnClickListener() {

   @Override
   public void onClick(DialogInterface dialog, int which) {
    dialog.dismiss();

    Main.this.finish();
   }
  });

  builder.setNegativeButton("取消", new OnClickListener() {

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

  builder.create().show();
 }

 

在onKeyDown(int keyCode, KeyEvent event)方法中调用此方法

public boolean onKeyDown(int keyCode, KeyEvent event) {
  if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
   dialog();
  }
  return false;
 }

  图2效果:改变了对话框的图表,添加了三个按钮

Dialog dialog = new AlertDialog.Builder(this).setIcon(
     android.R.drawable.btn_star).setTitle("喜好调查").setMessage(
     "你喜欢李连杰的电影吗?").setPositiveButton("很喜欢",
     new OnClickListener() {

      @Override
      public void onClick(DialogInterface dialog, int which) {
       // TODO Auto-generated method stub
       Toast.makeText(Main.this, "我很喜欢他的电影。",
         Toast.LENGTH_LONG).show();
      }
     }).setNegativeButton("不喜欢", new OnClickListener() {

    @Override
    public void onClick(DialogInterface dialog, int which) {
     // TODO Auto-generated method stub
     Toast.makeText(Main.this, "我不喜欢他的电影。", Toast.LENGTH_LONG)
       .show();
    }
   }).setNeutralButton("一般", new OnClickListener() {

    @Override
    public void onClick(DialogInterface dialog, int which) {
     // TODO Auto-generated method stub
     Toast.makeText(Main.this, "谈不上喜欢不喜欢。", Toast.LENGTH_LONG)
       .show();
    }
   }).create();

   dialog.show();

 

图3效果:信息内容是一个简单的View类型

new AlertDialog.Builder(this).setTitle("请输入").setIcon(
     android.R.drawable.ic_dialog_info).setView(
     new EditText(this)).setPositiveButton("确定", null)
     .setNegativeButton("取消", null).show();

 

图4效果:信息内容是一组单选框

new AlertDialog.Builder(this).setTitle("复选框").setMultiChoiceItems(
     new String[] { "Item1", "Item2" }, null, null)
     .setPositiveButton("确定", null)
     .setNegativeButton("取消", null).show();

 

图5效果:信息内容是一组多选框

new AlertDialog.Builder(this).setTitle("单选框").setIcon(
     android.R.drawable.ic_dialog_info).setSingleChoiceItems(
     new String[] { "Item1", "Item2" }, 0,
     new DialogInterface.OnClickListener() {
      public void onClick(DialogInterface dialog, int which) {
       dialog.dismiss();
      }
     }).setNegativeButton("取消", null).show();

 

图6效果:信息内容是一组简单列表项

new AlertDialog.Builder(this).setTitle("列表框").setItems(
     new String[] { "Item1", "Item2" }, null).setNegativeButton(
     "确定", null).show();

 

图7效果:信息内容是一个自定义的布局

1.布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_height="wrap_content" android:layout_width="wrap_content"
 android:background="#ffffffff" android:orientation="horizontal"
 android:id="@+id/dialog">
 <TextView android:layout_height="wrap_content"
   android:layout_width="wrap_content"
  android:id="@+id/tvname" android:text="姓名:" />
 <EditText android:layout_height="wrap_content"
  android:layout_width="wrap_content" android:id="@+id/etname" android:minWidth="100dip"/>

</LinearLayout>

2.调用代码

LayoutInflater inflater = getLayoutInflater();
   View layout = inflater.inflate(R.layout.dialog,
     (ViewGroup) findViewById(R.id.dialog));

   new AlertDialog.Builder(this).setTitle("自定义布局").setView(layout)
     .setPositiveButton("确定", null)
     .setNegativeButton("取消", null).show();

本教程我们来看看Android开发布局中的include标签、merge标签和ViewStub标签,利用这几个标签,我们可以优化我们的界面布局。

一、使用include标签将可复用的组件抽取出来(引用布局)


二、使用merge标签减少布局的嵌套层次(merge相当于framelayout)

场景1:布局根结点是FrameLayout且不需要设置background或padding等属性,可以用merge代替。
场景2:某布局作为子布局被其他布局include时,使用merge当作该布局的顶节点,这样在被引入时,顶结点会自动被忽略。


三、使用ViewStub标签来加载一些不常用的布局

作用:ViewStub标签同include标签一样可以用来引入一个外部布局,不同的是,ViewStub引入的布局默认不会扩张,既不会占用显示也不会占用位置,从而在解析Layout时节省cpu和内存


示例一:include


下面是res/layout/title.xml 布局文件:


<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="@drawable/title_bg" >  
 
   <Button  
        android:id="@+id/title_back"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:layout_gravity="center"  
        android:layout_margin="5dp"  
        android:background="@drawable/back_bg"  
        android:text="Back"  
        android:textColor="#fff" />  
 
   <TextView  
        android:id="@+id/title_text"  
        android:layout_width="0dp"  
        android:layout_height="wrap_content"  
        android:layout_gravity="center"  
        android:layout_weight="1"  
        android:gravity="center"  
        android:text="This is Title"  
        android:textColor="#fff"  
        android:textSize="25sp" />  
 
   <Button  
        android:id="@+id/title_edit"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:layout_gravity="center"  
        android:layout_margin="5dp"  
        android:background="@drawable/edit_bg"  
        android:text="Edit"  
        android:textColor="#fff" />  
 
 


下面是res/layout/activity_main.xml 布局文件:

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


示例二:merge


下面是res/layout/progress.xml 布局文件:


 
<merge xmlns:android="http://schemas.android.com/apk/res/android"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    android:orientation="vertical" >  
 
   <ProgressBar  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:layout_gravity="center" />  
 
 


下面是res/layout/activity_main.xml 布局文件:


<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:orientation="vertical" >  
 
   <FrameLayout  
        android:layout_width="match_parent"  
        android:layout_height="wrap_content" >  
 
       <TextView  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"  
            android:text="正文内容"  
            android:textSize="25sp" />  
 
        
    
 
 


示例三:ViewStub

下面是res/layout/activity_main.xml 布局文件:

<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:orientation="vertical" >  
 
   <Button   
        android:id="@+id/button"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:text="显示隐藏内容"/>  
      
   <ViewStub   
        android:id="@+id/id_viewStub"  
        android:layout_width="match_parent"  
        android:layout_height="wrap_content"  
        android:layout="@layout/title"/>  



下面是MainActivity.java主界面文件:


public class MainActivity extends Activity {  
    private Button b;  
    private ViewStub stub;  
 
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        b=(Button) findViewById(R.id.button);  
        stub=(ViewStub) findViewById(R.id.id_viewStub);  
        b.setOnClickListener(new OnClickListener() {  
            public void onClick(View v) {  
                stub.inflate();  
            }  
        });  
    }  

标签:[!--infotagslink--]

您可能感兴趣的文章: