1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settingslib.widget; 18 19 import android.content.Context; 20 import android.content.res.TypedArray; 21 import android.util.AttributeSet; 22 import android.view.LayoutInflater; 23 import android.view.View; 24 import android.view.ViewGroup; 25 import android.widget.FrameLayout; 26 27 import androidx.core.content.res.TypedArrayUtils; 28 import androidx.preference.Preference; 29 import androidx.preference.PreferenceViewHolder; 30 31 /** 32 * A preference can be simply customized a view by adding layout attribute in xml. 33 * User also can decide whether or not LayoutPreference allows above divider or below divider. 34 * 35 * For instances, 36 * 37 * <com.android.settingslib.widget.LayoutPreference 38 * ... 39 * android:layout="@layout/settings_entity_header" 40 * xxxxxxx:allowDividerAbove="true" 41 * xxxxxxx:allowDividerBelow="true" 42 * 43 */ 44 public class LayoutPreference extends Preference { 45 46 private final View.OnClickListener mClickListener = v -> performClick(v); 47 private boolean mAllowDividerAbove; 48 private boolean mAllowDividerBelow; 49 private View mRootView; 50 51 /** 52 * Constructs a new LayoutPreference with the given context's theme and the supplied 53 * attribute set. 54 * 55 * @param context The Context the view is running in, through which it can 56 * access the current theme, resources, etc. 57 * @param attrs The attributes of the XML tag that is inflating the view. 58 */ LayoutPreference(Context context, AttributeSet attrs)59 public LayoutPreference(Context context, AttributeSet attrs) { 60 super(context, attrs); 61 init(context, attrs, 0 /* defStyleAttr */); 62 } 63 64 /** 65 * Constructs a new LayoutPreference with the given context's theme, the supplied 66 * attribute set, and default style attribute. 67 * 68 * @param context The Context the view is running in, through which it can 69 * access the current theme, resources, etc. 70 * @param attrs The attributes of the XML tag that is inflating the view. 71 * @param defStyleAttr An attribute in the current theme that contains a 72 * reference to a style resource that supplies default 73 * values for the view. Can be 0 to not look for 74 * defaults. 75 */ LayoutPreference(Context context, AttributeSet attrs, int defStyleAttr)76 public LayoutPreference(Context context, AttributeSet attrs, int defStyleAttr) { 77 super(context, attrs, defStyleAttr); 78 init(context, attrs, defStyleAttr); 79 } 80 81 /** 82 * Constructs a new LayoutPreference with the given context's theme and a customized view id. 83 * 84 * @param context The Context the view is running in, through which it can 85 * access the current theme, resources, etc. 86 * @param resource The view id which you expected to be inflated and show in preference. 87 */ LayoutPreference(Context context, int resource)88 public LayoutPreference(Context context, int resource) { 89 this(context, LayoutInflater.from(context).inflate(resource, null, false)); 90 } 91 92 /** 93 * Constructs a new LayoutPreference with the given context's theme and a customized view. 94 * 95 * @param context The Context the view is running in, through which it can 96 * access the current theme, resources, etc. 97 * @param view The view which you expected show in preference. 98 */ LayoutPreference(Context context, View view)99 public LayoutPreference(Context context, View view) { 100 super(context); 101 setView(view); 102 } 103 init(Context context, AttributeSet attrs, int defStyleAttr)104 private void init(Context context, AttributeSet attrs, int defStyleAttr) { 105 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Preference); 106 mAllowDividerAbove = TypedArrayUtils.getBoolean(a, R.styleable.Preference_allowDividerAbove, 107 R.styleable.Preference_allowDividerAbove, false); 108 mAllowDividerBelow = TypedArrayUtils.getBoolean(a, R.styleable.Preference_allowDividerBelow, 109 R.styleable.Preference_allowDividerBelow, false); 110 a.recycle(); 111 112 a = context.obtainStyledAttributes( 113 attrs, R.styleable.Preference, defStyleAttr, 0); 114 int layoutResource = a.getResourceId(R.styleable.Preference_android_layout, 0); 115 if (layoutResource == 0) { 116 throw new IllegalArgumentException("LayoutPreference requires a layout to be defined"); 117 } 118 a.recycle(); 119 120 // Need to create view now so that findViewById can be called immediately. 121 final View view = LayoutInflater.from(getContext()) 122 .inflate(layoutResource, null, false); 123 setView(view); 124 } 125 setView(View view)126 private void setView(View view) { 127 setLayoutResource(R.layout.layout_preference_frame); 128 mRootView = view; 129 setShouldDisableView(false); 130 } 131 132 @Override onBindViewHolder(PreferenceViewHolder holder)133 public void onBindViewHolder(PreferenceViewHolder holder) { 134 holder.itemView.setOnClickListener(mClickListener); 135 136 final boolean selectable = isSelectable(); 137 holder.itemView.setFocusable(selectable); 138 holder.itemView.setClickable(selectable); 139 holder.setDividerAllowedAbove(mAllowDividerAbove); 140 holder.setDividerAllowedBelow(mAllowDividerBelow); 141 142 FrameLayout layout = (FrameLayout) holder.itemView; 143 layout.removeAllViews(); 144 ViewGroup parent = (ViewGroup) mRootView.getParent(); 145 if (parent != null) { 146 parent.removeView(mRootView); 147 } 148 layout.addView(mRootView); 149 } 150 151 /** 152 * Finds the view with the given ID. 153 * 154 * @param id the ID to search for 155 * @return a view with given ID if found, or {@code null} otherwise 156 */ findViewById(int id)157 public <T extends View> T findViewById(int id) { 158 return mRootView.findViewById(id); 159 } 160 161 /** 162 * LayoutPreference whether or not allows to set a below divider. 163 */ setAllowDividerBelow(boolean allowed)164 public void setAllowDividerBelow(boolean allowed) { 165 mAllowDividerBelow = allowed; 166 } 167 168 /** 169 * Return a value whether or not LayoutPreference allows to set a below divider. 170 */ isAllowDividerBelow()171 public boolean isAllowDividerBelow() { 172 return mAllowDividerBelow; 173 } 174 } 175