Android 主題資源風格定製

無論是定製系統還是自行開發APP的UI,其無論是使用標準UI還是自定義UI,最終都是需要自己熟悉主題風格的各種屬性設置,不過屬性非常的多,如果需要知道某個UI可以臨時查看一下SDK的


D:\liuzhibao\Android\sdk\platforms\android-N\data\res路徑下的,但是這個是純粹的資源文件,沒有java文件,所以還是推薦repo下來framework/base代碼.

下面先看看自定義View如何添加屬性之類的:

新建一個PumpKinCustomeView的android studio工程:

主類程序:

package org.durian.pumpkincustomeview;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class PumpKinMainActivity extends AppCompatActivity {

    private Button mButton;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pump_kin_main);

        mButton=(Button)findViewById(R.id.button);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent=new Intent(PumpKinMainActivity.this,ThemeActivity.class);
                PumpKinMainActivity.this.startActivity(intent);
                finish();
            }
        });

    }

}

既然要自定義View,那麼就需要新建一個atts.xml文件到res/values下,下面隨便舉幾個:

<?xml version="1.0" encoding="utf-8"?>
<resources>

<declare-styleable name="UIView">
    <attr name="pumpkin_background" format="color"/>
    <attr name="pumpkin_textcolor" format="color" />
    <attr name="pumpkin_textsize" format="integer"/>
    <attr name="pumpkin_view_width" format="dimension"/>
    <attr name="pumpkin_view_height" format="dimension"/>
    <attr name="pumpkin_text" format="string"/>
    <attr name="pumpkin_draw" format="reference"/>
    <attr name="pumpkin_enable" format="boolean"/>
    <attr name="pumpkin_style" format="reference" />
    <attr name="pumpkin_enum" format="enum" />
    <attr name="pumpkin_float" format="float" />
    <attr name="pumpkin_fraction" format="fraction" />
</declare-styleable>

</resources>

上面基本上一看還是非常清楚的,上面只缺少flag位運算format,fraction爲百分比,reference爲引用,enum可以如下:

<declare-styleable name="pumpkin_enum_style">
<attr name="pumpkin_enum">
    <enum name="enum1">1</enum>
    <enum name="enum2">2</enum>
    <enum name="enum3">3</enum>
</attr>
</declare-styleable>


定義好上面的屬性後,新建一個UIView的繼承View的類:

package org.durian.pumpkincustomeview.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;

import org.durian.pumpkincustomeview.R;

/**
 * Project name : PumpKinCustomeView
 * Created by zhibao.liu on 2016/4/28.
 * Time : 10:32
 * Email [email protected]
 * Action : durian
 */
public class UIView extends View {

    private Paint mPaint;
    private Paint mTextPaint;

    private int rectWidth;
    private int rectHeight;

    public UIView(Context context) {
        super(context);
    }

    public UIView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView(context);
        TypedArray typedArray=context.obtainStyledAttributes(attrs, R.styleable.UIView);

        int textSize=typedArray.getInteger(R.styleable.UIView_pumpkin_textsize,128);
        int textColor=typedArray.getColor(R.styleable.UIView_pumpkin_textcolor, Color.GRAY);

        mTextPaint.setColor(textColor);
        mTextPaint.setStrokeWidth(2);
        mTextPaint.setTextSize(textSize);

        int backgroundColor=typedArray.getColor(R.styleable.UIView_pumpkin_background,Color.GRAY);
        rectWidth=(int)typedArray.getDimension(R.styleable.UIView_pumpkin_view_width,512);
        rectHeight=(int)typedArray.getDimension(R.styleable.UIView_pumpkin_view_height,513);
        rectWidth=Utils.px2dip(context,rectWidth);
        rectHeight=Utils.px2dip(context,rectHeight);
        //rectHeight=Utils.dip2px(context,rectHeight);
        mPaint.setColor(backgroundColor);
        android.util.Log.i("UIVIEW","rectWidth : "+rectWidth+" rectHeight : "+rectHeight);

    }

    public void initView(Context context){
        mPaint=new Paint();
        mTextPaint=new Paint();
    }

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);

        canvas.drawRect(new Rect(10,10,rectWidth,rectHeight),mPaint);

        canvas.drawText("hello view",50,800,mTextPaint);

    }


}

然後在工程主Activity的佈局中添加這個自定義View.在添加之前,先爲這個View寫一個自定義style:

<!-- add by pumpkin custom style -->
    <style name="Pumpkin_style">
        <item name="pumpkin_background">#ffff0000</item>
        <item name="pumpkin_textcolor">#ff00ff00</item>
        <item name="pumpkin_view_width">500dp</item>
        <item name="pumpkin_view_height">500dp</item>

    </style>


然後添加到主Activity的佈局中:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:pumpkin="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context="org.durian.pumpkincustomeview.PumpKinMainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="theme"/>

    <org.durian.pumpkincustomeview.view.UIView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        pumpkin:pumpkin_textsize="128"
        style="@style/Pumpkin_style"
        />

</LinearLayout>


注意:

<org.durian.pumpkincustomeview.view.UIView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        pumpkin:pumpkin_textsize="128"
        style="@style/Pumpkin_style"
        />

這裏故意分開一部分直接寫在佈局中,有一部分屬性賦值封裝到PumKin_style,但是直接寫到佈局中需要注意比如(pumpkin:pumpkin_textsize):

在佈局頭部需要標記路徑:現在android studio可以直接res-auto,以前需要換成res/報名

xmlns:pumpkin=<a target=_blank href="http://schemas.android.com/apk/res-auto">http://schemas.android.com/apk/res-auto</a>

在自定義UIView中注意:

rectWidth=Utils.px2dip(context,rectWidth);
        rectHeight=Utils.px2dip(context,rectHeight);

注意: 我們在風格中明明寫着是500,但是如果沒有上面兩句程序,RectWidth的值將是1500,也就是這個地方設置的500dp(預想這樣),但是打印log出來將是1500px,結果整個屏幕的方形將非常的大,也不是我們所需要的,所以獲取尺寸以後需要轉一下從px轉到pd單位,所以這個地方需要特別注意.字體也存在這個轉換,但是上面程序沒有在給出了,轉換一般如下:

package org.durian.pumpkincustomeview.view;

import android.content.Context;

/**
 * Project name : PumpKinCustomeView
 * Created by zhibao.liu on 2016/4/28.
 * Time : 14:42
 * Email [email protected]
 * Action : durian
 */
public class Utils {

    public static int dip2px(Context context, float dipValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dipValue * scale + 0.5f);
    }

    public static int px2dip(Context context, float pxValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }

}


運行結果:



下面來看看如果是系統UI的屬性如何修改:下面android5.0來簡說,

加入我的Activity有CheckBox和EditText,我希望這個Activity中所有的CheckBox都是一致的,所有的EditText風格也是一致的,如何處理呢?

系統資源設計有幾個基本原則和規律:

<1> : 具有繼承性:如

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

使用parent關鍵字,繼承parent的所有屬性值.

<2> : 子類屬性覆蓋父類屬性值:即在雖然在父類對某個屬性賦值了,但是子類希望在這個屬性值上面不同於父類,那麼就在子類重新對改屬性值賦值,如下:

<style name="CustomeAppTheme" parent="@style/AppTheme">

        <item name="colorPrimary">#ffff0000</item>
        <!--<item name="colorAccent">#fff4e80b</item>-->
        <item name="editTextStyle">@style/pumpkin_editTextStyle</item>
        <item name="checkboxStyle">@style/pumpkin_checkboxStyle</item>

    </style>

屬性colorPrimary在父類AppTheme中已經賦了值,但還是我希望自己的Activity的titlebar的顏色是紅色,那麼在這裏就重新賦值,達到覆蓋父類那個屬性值的目的.

<3> : 回答最上面那個問題,如果需要調整一個頁面內所以某個UI的風格,那麼就需要重新將UI的風格重新寫,但是一般推薦繼承父類,只調整需要調整的部分,其他的仍然採用父類的屬性,這樣就不要從零開始寫了:

<style name="pumpkin_editTextStyle" parent="@android:style/Widget.Material.EditText">
        <item name="android:textColorPrimary">#ffff0000</item>
        <item name="android:textColor">#ff11e7f3</item>
        <item name="android:textSize">48sp</item>
    </style>

    <style name="pumpkin_checkboxStyle" parent="@android:style/Widget.Material.CompoundButton.CheckBox">
        <item name="android:textColor">#fff107f3</item>
        <item name="android:textSize">32sp</item>
    </style>

將頁面的EditText和CheckBox的幾個屬性重新調整.

在新建一個Activity測試一下:

package org.durian.pumpkincustomeview;

import android.content.Intent;
import android.content.res.Resources;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class ThemeActivity extends AppCompatActivity {

    private Button button;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_theme);

        button=(Button)findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent=new Intent(ThemeActivity.this,CompareThemeActivity.class);
                ThemeActivity.this.startActivity(intent);
            }
        });

    }

}

manifest.xml添加自定義主題給它:

<activity
            android:name=".ThemeActivity"
            android:theme="@style/CustomeAppTheme"></activity>

對應的佈局:

<?xml version="1.0" encoding="utf-8"?>
<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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context="org.durian.pumpkincustomeview.ThemeActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="hello textview"/>

    <CheckBox
        android:text="hello checkbox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <EditText
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/button"
        android:text="button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>


運行結果:


發現顏色有54%透明度的黑色變爲指定的顏色了.

有讀者可能又會問題,如何知道EditText等的屬性風格,比如

<item name="editTextStyle">

這個地方記住順序:

打開android源代碼,查看EditText.java源代碼:

public EditText(Context context, AttributeSet attrs) {
        this(context, attrs, com.android.internal.R.attr.editTextStyle);
    }

看到了吧,這就是爲什麼前面要自己先寫一個自定義的UIView,回顧一下這些主題風格是如何被UI使用的,看到上面的editTextStyle,然後搜索:

        <item name="editTextStyle">@style/Widget.Material.EditText</item>
我的是android5.0的代碼,所以帶有material.

同時搜索的時候一定要注意,這個搜索的關鍵字一定要出現在Public.xml中,因爲android系統後面的資源需要聲明,如果聲明在Public.xml中才能夠被APP developer使用,即公共的,如果是在symbol.xml,那就算是系統內部自己使用,即私有的.很顯然我們這裏是需要在APP中使用,所以一定是需要公有的纔行.在自己APP程序重新再寫這些屬性值即可覆蓋framework中預設的.

假如自己的UI的一些屬性在系統中聲明爲私有的,怎麼辦,最粗暴的做法就是直接將系統中的風格和資源文件全部挑選出來放到自己的APP資源下,單個UI的話並不是很難.

假設,這裏只是個假設SeekBar 這個ui風格是私有的,那麼步驟如下:

<1> : 查看SeekBar.java這個類:

public SeekBar(Context context, AttributeSet attrs) {
        this(context, attrs, com.android.internal.R.attr.seekBarStyle);
    }

繼續看seekBarStyle風格:

<item name="seekBarStyle">@style/Widget.Material.SeekBar</item>

找到"定義處":

<style name="Widget.Material.SeekBar">
        <item name="indeterminateOnly">false</item>
        <item name="progressDrawable">@drawable/seekbar_track_material</item>
        <item name="indeterminateDrawable">@drawable/seekbar_track_material</item>
        <item name="thumb">@drawable/seekbar_thumb_material_anim</item>
        <item name="splitTrack">true</item>
        <item name="useDisabledAlpha">false</item>
        <item name="focusable">true</item>
        <item name="paddingStart">16dip</item>
        <item name="paddingEnd">16dip</item>
        <item name="mirrorForRtl">true</item>
        <item name="background">@drawable/control_background_32dp_material</item>
    </style>

其實一般改的並不多,一般是修改SeekBar的進度線的風格(粗細,顏色等)以及遊標圖案或者顏色,將上面的copy到自己的工程

假如修改背景顏色,那麼就還需要提取這個資源:

@drawable/control_background_32dp_material

這個想都不用想,絕對是個xml文件:

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2015 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->

<ripple xmlns:android="http://schemas.android.com/apk/res/android"
        android:color="@color/control_highlight_material"
        android:radius="16dp" />

一看,control_highlight_material還是一個xml:

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2014 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_checked="true"
          android:state_enabled="true"
          android:alpha="@dimen/highlight_alpha_material_colored"
          android:color="?attr/colorControlActivated" />
    <item android:color="?attr/colorControlHighlight" />
</selector>

也需要提取出來.繼續查找顏色:

        <item name="colorControlActivated">?attr/colorAccent</item>

一看colorAccent這個顏色值,這個顏色值很瘋狂的,是一個全局的,比如你在自己的APP主題中設置:

<item name="colorAccent">#fff4e80b</item>

你會發現你的所有UI都變色了,而不是一個.

繼續查看另外一個顏色colorCotrolHighlight:

<item name="colorControlHighlight">@color/ripple_material_dark</item>

這個顏色值也是一個xml:

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2015 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:alpha="@dimen/highlight_alpha_material_dark"
          android:color="@color/foreground_material_dark" />
</selector>

再來看看foreground_material_dark,最終是

<color name="foreground_material_dark">@color/white</color>

即白色前景.

瞭解上面的流程,如果參考系統的UI就可以拷貝出來自行調整各自的值.


當然上面都是調整APP部分的屬性,如果是定製系統,那麼就可以在vendor/overlay/framework中自行開發,這樣整個系統都有了,但是一般這樣的都要拷貝系統原生的,如果希望自己定製的系統中運行的第三方app也跟着調整,也可以直接改原生的主題風格,將原生的代碼改掉,當然一般不推薦.


後面一篇可以看看系統基本UI都是誰演化而來:

Button extends TextView 
TextView extends View 


















































發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章