自定义Preference

来源:互联网 发布:永安 知乎 编辑:程序博客网 时间:2024/06/03 16:28

Preference、PreferenceActivity,PreferenceFragment

Preference的容器,如PreferenceGroup持有一个ListView对象成员,而adapter是PreferenceGroupAdapter(extends BaseAdapter)

PreferenceGroupAdapter#getView()

public View getView(int position, View convertView, ViewGroup parent) {        final Preference preference = this.getItem(position);//获取对应的Preference        // Build a PreferenceLayout to compare with known ones that are cacheable.        mTempPreferenceLayout = createPreferenceLayout(preference, mTempPreferenceLayout);        // If it's not one of the cached ones, set the convertView to null so that         // the layout gets re-created by the Preference.        if (Collections.binarySearch(mPreferenceLayouts, mTempPreferenceLayout) < 0 ||                (getItemViewType(position) == getHighlightItemViewType())) {            convertView = null;        }        View result = preference.getView(convertView, parent);//调用Preference的getView获取view        if (position == mHighlightedPosition && mHighlightedDrawable != null) {            ViewGroup wrapper = new FrameLayout(parent.getContext());            wrapper.setLayoutParams(sWrapperLayoutParams);            wrapper.setBackgroundDrawable(mHighlightedDrawable);            wrapper.addView(result);            result = wrapper;        }        return result;    }

Preference#getView()

/**     * Gets the View that will be shown in the {@link PreferenceActivity}.     *      * @param convertView The old View to reuse, if possible. Note: You should     *            check that this View is non-null and of an appropriate type     *            before using. If it is not possible to convert this View to     *            display the correct data, this method can create a new View.     * @param parent The parent that this View will eventually be attached to.     * @return Returns the same Preference object, for chaining multiple calls     *         into a single statement.     * @see #onCreateView(ViewGroup)     * @see #onBindView(View)     */    public View getView(View convertView, ViewGroup parent) {        if (convertView == null) {            convertView = onCreateView(parent);//调用onCreateView去inflate布局        }        onBindView(convertView);//将Preference的xml文件的属性应用到View中        return convertView;    }

Preference#onCreateView()

/**     * Creates the View to be shown for this Preference in the     * {@link PreferenceActivity}. The default behavior is to inflate the main     * layout of this Preference (see {@link #setLayoutResource(int)}. If     * changing this behavior, please specify a {@link ViewGroup} with ID     * {@link android.R.id#widget_frame}.     * <p>     * Make sure to call through to the superclass's implementation.     *      * @param parent The parent that this View will eventually be attached to.     * @return The View that displays this Preference.     * @see #onBindView(View)     */    @CallSuper    protected View onCreateView(ViewGroup parent) {        final LayoutInflater layoutInflater =            (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);                final View layout = layoutInflater.inflate(mLayoutResId, parent, false);//布局为mLayoutResId                 final ViewGroup widgetFrame = (ViewGroup) layout                .findViewById(com.android.internal.R.id.widget_frame);        if (widgetFrame != null) {            if (mWidgetLayoutResId != 0) {//mWidgeLayoutResId可以在子类构造方法中传入一个style                layoutInflater.inflate(mWidgetLayoutResId, widgetFrame);            } else {                widgetFrame.setVisibility(View.GONE);            }        }        return layout;    }
该方法是inflate一个Preference的layout,而想要自定义layout,可以重写这个onCreate方法,也可以不重写,

而在子类构造方法中传入一个参数到Preference的第三个参数defStyleAttr。详细看下面SwichPreference的

解析。



Preference#onBindView()

/**     * Binds the created View to the data for this Preference.     * <p>     * This is a good place to grab references to custom Views in the layout and     * set properties on them.     * <p>     * Make sure to call through to the superclass's implementation.     *      * @param view The View that shows this Preference.     * @see #onCreateView(ViewGroup)     */    @CallSuper    protected void onBindView(View view) {        final TextView titleView = (TextView) view.findViewById(com.android.internal.R.id.title);        if (titleView != null) {            final CharSequence title = getTitle();//获取title属性值            if (!TextUtils.isEmpty(title)) {                titleView.setText(title);                titleView.setVisibility(View.VISIBLE);            } else {                titleView.setVisibility(View.GONE);            }        }        final TextView summaryView = (TextView) view.findViewById(                com.android.internal.R.id.summary);        if (summaryView != null) {            final CharSequence summary = getSummary();//获取summary属性值            if (!TextUtils.isEmpty(summary)) {                summaryView.setText(summary);                summaryView.setVisibility(View.VISIBLE);            } else {                summaryView.setVisibility(View.GONE);            }        }        final ImageView imageView = (ImageView) view.findViewById(com.android.internal.R.id.icon);        if (imageView != null) {            if (mIconResId != 0 || mIcon != null) {                if (mIcon == null) {                    mIcon = getContext().getDrawable(mIconResId);                }                if (mIcon != null) {                    imageView.setImageDrawable(mIcon);                }            }            imageView.setVisibility(mIcon != null ? View.VISIBLE : View.GONE);        }        final View imageFrame = view.findViewById(com.android.internal.R.id.icon_frame);        if (imageFrame != null) {            imageFrame.setVisibility(mIcon != null ? View.VISIBLE : View.GONE);        }        if (mShouldDisableView) {            setEnabledStateOnViews(view, isEnabled());//和Enable属性相关,下面在自定义SwitchPreference时会详细说        }    }
该方法在Preference属性改变时,会被调用,因为getView会被调用,因为属性改变后,View也会改变,如一些可见性等。

在自定义Preference时,一定要调用这个方法,因为里面有些被调用到的方法的访问权限是private的,如果不调用这个

父类方法,有些属性就无法生效。


还介绍一个public方法,performClick()

/**     * Called when a click should be performed.     *      * @param preferenceScreen A {@link PreferenceScreen} whose hierarchy click     *            listener should be called in the proper order (between other     *            processing). May be null.     * @hide     */    public void performClick(PreferenceScreen preferenceScreen) {                if (!isEnabled()) {//当enalble时,此方法无效。enalbe的确认其属性enable只是必要条件            return;        }                onClick();//自定义时,最好不要直接重写PerformClick,可以重写这个空方法                if (mOnClickListener != null && mOnClickListener.onPreferenceClick(this)) {            return;//对mOnClickListener的处理可能需要重写该方法        }                PreferenceManager preferenceManager = getPreferenceManager();        if (preferenceManager != null) {            PreferenceManager.OnPreferenceTreeClickListener listener = preferenceManager                    .getOnPreferenceTreeClickListener();            if (preferenceScreen != null && listener != null                    && listener.onPreferenceTreeClick(preferenceScreen, this)) {                return;            }        }                if (mIntent != null) {            Context context = getContext();            context.startActivity(mIntent);        }    }
该方法是点击该Preference的view时会回调的一个方法,如果必须重写该方法,最好调用一下这个父类方法


Perference的一个构造方法:

public SwitchPreference(Context context, AttributeSet attrs) {        this(context, attrs, com.android.internal.R.attr.switchPreferenceStyle);    }
其中switchPreferenceStyle
<item name="switchPreferenceStyle">@style/Preference.SwitchPreference</item>

而perference.SwitchPreference

<style name="Preference.SwitchPreference">        <item name="widgetLayout">@layout/preference_widget_switch</item>        <item name="switchTextOn">@string/capital_on</item>        <item name="switchTextOff">@string/capital_off</item>    </style>
其中的widgetlayout就是Perference中的mWidgetLayoutResId。




下面说下SwitchPreference的实现,由此可知道自定义Preference的方法

SwitchPreference extends TwoStatePreference,TwoStatePreference没有定义一些跟view相关的东西,

只是增加了mChecked,mSummaryOff,mSummaryOn字段及其getter和setter。而在setter实现中,没有实现对View

的任何操作。其实在Perference的实现中,属性的setter中也没有对View操作,只是将设置进来的东西保存到成员

中,然后notifyChange()通知一个Listener,就是OnPerferenceChangeInternalListener,这是一个默认权限

的接口,而其setter也是,所以只有同一个包的才能设置这个接口,所以我们无法通过子类去设置或者重写这个Listener。

而PreferenceGroupAdapter是实现并为该Group的每个Preference设置了这个Listener。

其实现如下:

public void onPreferenceChange(Preference preference) {        notifyDataSetChanged();    }

所以每次有什么属性变化,都会ListView的Adapter都会调用所有Preference#getView一遍。

所以在自定义时,需要在onBindView中处理自定义Preference中新增属性的变化,并把变化应用到

View中,也可以在onBindView中处理其他属性变化。


Preference的Enable属性

setEnable

/**     * Sets whether this Preference is enabled. If disabled, it will     * not handle clicks.     *      * @param enabled Set true to enable it.     */    public void setEnabled(boolean enabled) {        if (mEnabled != enabled) {            mEnabled = enabled;            // Enabled state can change dependent preferences' states, so notify            notifyDependencyChange(shouldDisableDependents());            notifyChanged();        }    }
看上面的Preference#onBindView()方法,要将Enable属性和View关联起来,代码如下:

if (mShouldDisableView) {            setEnabledStateOnViews(view, isEnabled());        }
就是还需要mShouldDisableView属性为true才行。而且setEnabledStateOnViews()方法是private,

不调用父类onBindView,根本没有实现Enable属性对View生效。而isEnable()方法,也不只是考虑Enable

属性,其代码如下:

boolean isEnable()

/**     * Checks whether this Preference should be enabled in the list.     *      * @return True if this Preference is enabled, false otherwise.     */    public boolean isEnabled() {        return mEnabled && mDependencyMet && mParentDependencyMet;    }
还考虑了该Preference及父容器的dependency属性。



原创粉丝点击