2015-02-15 33 views
10

Tôi đang cố gắng tạo chủ đề cho ứng dụng của mình trong Android. Tuy nhiên, mỗi widget lại là một nỗi đau bực mình: tôi phải tìm kiếm theming widget cụ thể đó và sau đó tạo ra một phong cách hy vọng xuất phát từ cùng một kiểu mà widget sử dụng.Cách khám phá kiểu dáng trong android

Tất nhiên, câu trả lời về việc theming một tiện ích cụ thể không phải lúc nào cũng chứa thông tin về kiểu cơ sở, chỉ các màu cụ thể.

Vì vậy, thay vì chấp nhận cá để ăn, bạn có thể dạy tôi cách câu cá không?

Làm cách nào để diễn giải các cuộc gọi ObtainStyledAttributes() trong các trình tạo tiện ích con và trích xuất các kiểu từ đó? Tôi làm cách nào để tái chế điều đó?

Cụ thể, bạn có thể hướng dẫn tôi qua màu nút AlertDialog không? Những gì phong cách xác định lollipop phẳng nút + màu văn bản teal? Làm thế nào để có được phong cách đó nếu tôi bắt đầu từ nguồn AlertDialog và cuộc gọi ObtainStyledAttributes?

Trả lời

27

Tôi thấy rằng kiểu dáng là về cách khóa theo cách của bạn thông qua khung. những gì (hầu như luôn luôn) xuất phát từ việc triển khai tiện ích con. nơi, tôi thấy là tất cả các nơi. Tôi sẽ cố gắng hết sức để giải thích quy trình thông qua trường hợp sử dụng cụ thể của bạn - nút của AlertDialog.

Bắt đầu tắt:

Bạn đã có này đã tìm ra: chúng ta bắt đầu với mã nguồn của widget. Chúng tôi đặc biệt cố gắng tìm - nơi các nút AlertDialog nhận được màu văn bản của họ. Vì vậy, chúng tôi bắt đầu bằng cách xem các nút này đến từ đâu. Chúng có được tạo ra một cách rõ ràng trong thời gian chạy không? Hoặc chúng được định nghĩa trong bố cục xml, đang được tăng cao?

Trong mã nguồn, chúng tôi thấy rằng mAlert xử lý các tùy chọn nút trong số những thứ khác:

public void setButton(int whichButton, CharSequence text, Message msg) { 
    mAlert.setButton(whichButton, text, null, msg); 
} 

mAlert là một thể hiện của AlertController. Trong constructor của nó, chúng ta thấy rằng thuộc tính alertDialogStyle xác định bố cục xml:

TypedArray a = context.obtainStyledAttributes(null, 
      com.android.internal.R.styleable.AlertDialog, 
      com.android.internal.R.attr.alertDialogStyle, 0); 

    mAlertDialogLayout = 
      a.getResourceId(
      com.android.internal.R.styleable.AlertDialog_layout, 
      com.android.internal.R.layout.alert_dialog); 

Vì vậy, cách bố trí chúng ta nên nhìn vào là alert_dialog.xml-[sdk_folder]/platforms/android-21/data/res/layout/alert_dialog.xml:

Cách bố trí xml là khá dài. Đây là phần có liên quan:

<LinearLayout> 

.... 
.... 

<LinearLayout android:id="@+id/buttonPanel" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:minHeight="54dip" 
    android:orientation="vertical" > 
    <LinearLayout 
     style="?android:attr/buttonBarStyle" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:orientation="horizontal" 
     android:paddingTop="4dip" 
     android:paddingStart="2dip" 
     android:paddingEnd="2dip" 
     android:measureWithLargestChild="true"> 
     <LinearLayout android:id="@+id/leftSpacer" 
      android:layout_weight="0.25" 
      android:layout_width="0dip" 
      android:layout_height="wrap_content" 
      android:orientation="horizontal" 
      android:visibility="gone" /> 
     <Button android:id="@+id/button1" 
      android:layout_width="0dip" 
      android:layout_gravity="start" 
      android:layout_weight="1" 
      style="?android:attr/buttonBarButtonStyle" 
      android:maxLines="2" 
      android:layout_height="wrap_content" /> 
     <Button android:id="@+id/button3" 
      android:layout_width="0dip" 
      android:layout_gravity="center_horizontal" 
      android:layout_weight="1" 
      style="?android:attr/buttonBarButtonStyle" 
      android:maxLines="2" 
      android:layout_height="wrap_content" /> 
     <Button android:id="@+id/button2" 
      android:layout_width="0dip" 
      android:layout_gravity="end" 
      android:layout_weight="1" 
      style="?android:attr/buttonBarButtonStyle" 
      android:maxLines="2" 
      android:layout_height="wrap_content" /> 
     <LinearLayout android:id="@+id/rightSpacer" 
      android:layout_width="0dip" 
      android:layout_weight="0.25" 
      android:layout_height="wrap_content" 
      android:orientation="horizontal" 
      android:visibility="gone" /> 
    </LinearLayout> 

Bây giờ chúng ta biết rằng các nút có được phong cách tổ chức bởi thuộc tính buttonBarButtonStyle.

Head qua [sdk_folder]/platforms/android-21/data/res/values/themes.material.xml và tìm kiếm buttonBarButtonStyle:

<!-- Defined under `<style name="Theme.Material">` --> 
<item name="buttonBarButtonStyle">@style/Widget.Material.Button.ButtonBar.AlertDialog</item> 

<!-- Defined under `<style name="Theme.Material.Light">` --> 
<item name="buttonBarButtonStyle">@style/Widget.Material.Light.Button.ButtonBar.AlertDialog</item> 

Tùy thuộc vào những gì chủ đề của cha mẹ hoạt động của bạn là, buttonBarButtonStyle sẽ đề cập đến một trong hai phong cách này. Bây giờ, hãy giả sử chủ đề hoạt động của bạn mở rộng Theme.Material.Chúng tôi sẽ xem xét @style/Widget.Material.Button.ButtonBar.AlertDialog:

mở [sdk_folder]/platforms/android-21/data/res/values/styles_material.xml và tìm kiếm Widget.Material.Button.ButtonBar.AlertDialog:

<!-- Alert dialog button bar button --> 
<style name="Widget.Material.Button.ButtonBar.AlertDialog" parent="Widget.Material.Button.Borderless.Colored"> 
    <item name="minWidth">64dp</item> 
    <item name="maxLines">2</item> 
    <item name="minHeight">@dimen/alert_dialog_button_bar_height</item> 
</style> 

Được rồi. Nhưng những giá trị này không giúp chúng ta xác định màu chữ của nút. Chúng ta nên nhìn vào phong cách cha mẹ tiếp theo - Widget.Material.Button.Borderless.Colored:

<!-- Colored borderless ink button --> 
<style name="Widget.Material.Button.Borderless.Colored"> 
    <item name="textColor">?attr/colorAccent</item> 
    <item name="stateListAnimator">@anim/disabled_anim_material</item> 
</style> 

Cuối cùng, chúng tôi tìm ra textColor - và cung cấp của mình bằng attr/colorAccent khởi tạo trong Theme.Material:

<item name="colorAccent">@color/accent_material_dark</item> 

Đối Theme.Material.Light, colorAccent được định nghĩa là:

<item name="colorAccent">@color/accent_material_light</item> 

Duyệt đến [sdk_folder]/platforms/android-21/data/res/values/colors_material.xml và định vị thứ màu sắc ese:

<color name="accent_material_dark">@color/material_deep_teal_200</color> 
<color name="accent_material_light">@color/material_deep_teal_500</color> 

<color name="material_deep_teal_200">#ff80cbc4</color> 
<color name="material_deep_teal_500">#ff009688</color> 

Ảnh chụp màn hình của một AlertDialog và văn bản màu tương ứng:

enter image description here

Shortcut:

Đôi khi, nó dễ dàng hơn để đọc các giá trị màu (như trong hình trên) và tìm kiếm bằng cách sử dụng AndroidXRef. Cách tiếp cận này sẽ không hữu ích trong trường hợp của bạn vì #80cbc4 sẽ chỉ chỉ ra rằng màu nhấn của nó. Bạn vẫn sẽ phải xác định vị trí Widget.Material.Button.Borderless.Colored và buộc nó với thuộc tính buttonBarButtonStyle.

Thay đổi nút của văn bản màu:

Lý tưởng nhất, chúng ta nên tạo ra một phong cách mà kéo dài Widget.Material.Button.ButtonBar.AlertDialog, ghi đè android:textColor bên trong nó, và gán nó vào thuộc tính buttonBarButtonStyle. Nhưng, điều này sẽ không hoạt động - dự án của bạn sẽ không biên dịch. Điều này là do Widget.Material.Button.ButtonBar.AlertDialog là kiểu không công khai và do đó không thể mở rộng. Bạn có thể xác nhận điều này bằng cách kiểm tra Link.

Chúng tôi sẽ làm điều tốt nhất tiếp theo - mở rộng phong cách mẹ của Widget.Material.Button.ButtonBar.AlertDialog-Widget.Material.Button.Borderless.Colored mà là công khai.

<style name="CusButtonBarButtonStyle" 
     parent="@android:style/Widget.Material.Button.Borderless.Colored"> 
    <!-- Yellow --> 
    <item name="android:textColor">#ffffff00</item> 

    <!-- From Widget.Material.Button.ButtonBar.AlertDialog --> 
    <item name="android:minWidth">64dp</item> 
    <item name="android:maxLines">2</item> 
    <item name="android:minHeight">@dimen/alert_dialog_button_bar_height</item> 
</style> 

Lưu ý rằng chúng tôi thêm 3 mục sau khi ghi đè android:textColor. Đây là từ phong cách ngoài công lập Widget.Material.Button.ButtonBar.AlertDialog. Vì chúng ta không thể mở rộng nó trực tiếp, chúng ta phải bao gồm các mục mà nó định nghĩa. Lưu ý: Giá trị dimen sẽ phải được tra cứu và chuyển đến res/values(-xxxxx)/dimens.xml tệp phù hợp trong dự án của bạn.

Kiểu CusButtonBarButtonStyle sẽ được gán cho thuộc tính buttonBarButtonStyle. Nhưng câu hỏi đặt ra là, một AlertDialog sẽ biết điều này như thế nào?Từ mã nguồn:

protected AlertDialog(Context context) { 
    this(context, resolveDialogTheme(context, 0), true); 
} 

Đi qua 0 như là đối số thứ hai cho resolveDialogTheme(Context, int) sẽ kết thúc trong mệnh đề else:

static int resolveDialogTheme(Context context, int resid) { 
    if (resid == THEME_TRADITIONAL) { 
     .... 
    } else { 
     TypedValue outValue = new TypedValue(); 
     context.getTheme().resolveAttribute(
       com.android.internal.R.attr.alertDialogTheme, 
       outValue, true); 
     return outValue.resourceId; 
    } 
} 

Chúng tôi bây giờ biết rằng chủ đề được tổ chức bởi alertDialogTheme thuộc tính. Tiếp theo, chúng tôi xem xét những gì alertDialogTheme trỏ tới. Giá trị của thuộc tính này sẽ phụ thuộc vào chủ đề gốc của hoạt động của bạn. Duyệt đến thư mục sdk của bạn và tìm số values/themes_material.xml bên trong android-21. Tìm kiếm alertDialogTheme. Kết quả:

<!-- Defined under `<style name="Theme.Material">` --> 
<item name="alertDialogTheme">@style/Theme.Material.Dialog.Alert</item> 

<!-- Defined under `<style name="Theme.Material.Light">` --> 
<item name="alertDialogTheme">@style/Theme.Material.Light.Dialog.Alert</item> 

<!-- Defined under `<style name="Theme.Material.Settings">` --> 
<item name="alertDialogTheme">@style/Theme.Material.Settings.Dialog.Alert</item> 

Vì vậy, dựa trên chủ đề cơ bản của hoạt động, alertDialogTheme sẽ giữ một trong 3 giá trị này. Để cho AlertDialog biết về CusButtonBarButtonStyle, chúng tôi cần ghi đè thuộc tính alertDialogTheme trong chủ đề của ứng dụng. Giả sử, chúng tôi đang sử dụng Theme.Material làm chủ đề cơ sở.

<style name="AppTheme" parent="android:Theme.Material"> 
    <item name="android:alertDialogTheme">@style/CusAlertDialogTheme</item> 
</style> 

Từ trên, chúng ta biết rằng alertDialogTheme điểm để Theme.Material.Dialog.Alertkhi chủ đề cơ bản của ứng dụng của bạn là Theme.Material. Vì vậy, nên có CusAlertDialogThemeTheme.Material.Dialog.Alert như mẹ của nó:

<style name="CusAlertDialogTheme" 
     parent="android:Theme.Material.Dialog.Alert"> 
    <item name="android:buttonBarButtonStyle">@style/CusButtonBarButtonStyle</item> 
</style> 

Kết quả:

enter image description here

Vì vậy, thay vì chấp nhận cá để ăn, có thể giúp bạn dạy tôi cá để thay thế?

Ít nhất, tôi hy vọng đã giải thích được cá ở đâu.

P.S. Tôi nhận ra tôi đã đăng một con voi ma mút.

+2

WOW, chỉ WOW. Đó là mớ hỗn độn mà tôi tưởng tượng ra. Google vẫn cần phải tìm hiểu nhiều về phát triển API: (Ít nhất Material cần cố gắng khắc phục những vấn đề này. Bây giờ, để xem liệu tôi có thể dịch những gì tôi đã học được ở đây sang chủ đề Appcompat mà tôi đang thực sự sử dụng hay không) – velis

+1

@velis lol, Hãy cho tôi biết nếu bạn có thể sử dụng phương pháp này trong việc tùy chỉnh chủ đề Appcompat.Nếu bạn đâm vào tường, chúng ta có thể thảo luận thêm. Nhân tiện, câu hỏi hay, và cảm ơn vì tiền thưởng. – Vikram

+0

trên android-24 tệp chủ đề là themes_material.xml – dazza5000

0

Ngoài câu trả lời tuyệt vời @Vikram, đáng lưu ý rằng Android Studio có thể đơn giản hóa công việc của bạn rất nhiều. Bạn chỉ cần di chuột qua Chủ đề, nó sẽ hiển thị nội dung như sau.

actionBarStyle = @style/Widget.AppCompat.Light.ActionBar.Solid 
=> @style/Widget.AppCompat.Light.ActionBar.Solid 

Bạn cũng có thể sử dụng click chuột để di chuyển giữa phong cách, giống như những gì bạn làm với mã java bình thường.

Và bạn có thể tìm res hỗ trợ thư viện trong <sdk root>/extras/android/m2repository/com/android/support/<support library name>/<version number>/<support library>.aar/res

Nhưng *.aar/res/values/values.xml chứa tất cả các giá trị, và nó không phải dễ đọc. Bạn có thể nhận được sự hỗ trợ mã thư viện gốc và các nguồn lực trong https://android.googlesource.com/platform/frameworks/support/+/master

Có một nút tên tgz tải ảnh chụp hiện tại.