从源码角度一步一步来修改PreferenceActivity界面

      

  PreferenceActivity给我们封装好了一个数据存储对象,我们只需要在xml文件中写上控件即可完成简单的设置界面。但是系统提供的设置界面十分的简陋,要想做的好看必须要自己来进行修改。本文就是一步一步教大家如何定义自己的PreferenceActivity界面。

 

一、创建模块一(选择模块组)

 

先在res/xml文件夹中定义一个customer_prefs.xml文件(名字自定),建立根节点

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
  ……

</PreferenceScreen>

 

我们的每一个选项组都是一个PreferenceCategory,所以我们在节点中定义这个标签。

<PreferenceCategory
        android:layout="@layout/prefs_category_widget"
        android:title="选择模块组" >

        <!-- 开关:定义了自定义的widget -->
        <CheckBoxPreference
            android:defaultValue="true"
            android:icon="@drawable/appstore"
            android:key="checkbox_preference"
            android:layout="@layout/prefs_list_s_item"
            android:title="开关"
            android:widgetLayout="@layout/checkbox_prefs_widget" />

        <!-- 单选:弹出一个单选list,选中的值会出现在摘要中 -->
        <com.kale.preference.MyListPreference
            android:dialogTitle="dialog_title_list_preference"
            android:entries="@array/floatColor"
            android:entryValues="@array/floatColor_value"
            android:icon="@drawable/itunes"
            android:key="list_preference"
            android:layout="@layout/prefs_list_s_item"
            android:summary="用单选列表来选择数据"
            android:title="单选列表" />
        <!-- 多选:弹出多选列表,选中的项目会出现在摘要中。这里的摘要由代码控制,所以请不要书写 -->
        <com.kale.preference.MyMultiSelectListPreference
            android:entries="@array/floatColor"
            android:entryValues="@array/floatColor_value"
            android:key="mult_preference"
            android:layout="@layout/prefs_list_s_item"
            android:title="多选列表" />
    </PreferenceCategory>

 我们注意到在PreferenceCategory节点中有android:layout属性,这个属性就是定义这个模块组的头部视图的。在这个layout中的textview的id必须是安卓自己的id,也就是title,这样我们在xml中设置的title就会对应到这个布局的文字上,才能有效!

<PreferenceCategory
        android:layout="@layout/prefs_category_widget"
        android:title="选择模块组" >

    </PreferenceCategory>


prefs_category_widget

 对应到布局就是->

<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="#ededed"
    android:orientation="vertical" >

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:src="#d9d9d9" />

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:layout_marginBottom="16dp"
        android:src="#e2e2e2" />

    <TextView
        android:id="@android:id/title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="2dp"
        android:paddingLeft="6dp"
        android:text="title"
        android:textColor="#939393"
        android:textSize="14sp" />

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="0.5dp"
        android:src="#e9e9e9" />

</LinearLayout>

接下来我们在PreferenceCategory里面写上了我们的控件,这里我们放的是一个checkbox,一个单选list,一个多选List

title:控件上的主文字

defaultValue:默认的值

icon:左边的图标

key:这个控件的key(必须有),系统是通过这个key来存放数据的

layout:这个控件的布局文件

<?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="wrap_content"
    android:background="@drawable/selector_item"
    android:gravity="center_vertical"
    android:minHeight="50dp"
    android:orientation="horizontal"
    android:paddingLeft="16dp"
    android:paddingRight="16dp" >

    <ImageView
        android:id="@android:id/icon"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_gravity="center_vertical"
        android:layout_marginRight="6dp"
        android:scaleType="fitCenter"
        android:src="@drawable/appstore" />

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="4dp"
        android:layout_marginTop="4dp" >

        <TextView
            android:id="@android:id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:ellipsize="marquee"
            android:fadingEdge="horizontal"
            android:singleLine="true"
            android:text="title"
            android:textColor="#4d4d4d"
            android:textSize="18.0sp" />
        
        <TextView
            android:id="@android:id/summary"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_alignParentRight="true"
            android:layout_toLeftOf="@android:id/widget_frame"
            android:maxLines="2"
            android:text="summary"
            android:textColor="#AAAAAA"
            android:textSize="14sp" />
        
        <FrameLayout
            android:id="@android:id/widget_frame"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:layout_marginLeft="4dp"
            android:gravity="center_vertical" >
        </FrameLayout>


    </RelativeLayout>

</LinearLayout>


widgetLayout:这个控件右边的一个布局中的布局文件,就是上面layout布局中,id为widget_frame的布局中要填入的布局。我在checkboxpreference中就定义了这个属性,放上了自定义的checkbox。这是布局文件:

checkbox_prefs_widget.xml

 

<?xml version="1.0" encoding="UTF-8"?>
<!-- 这里放上系统的id -->
<CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/checkbox"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_vertical"
    android:button="@drawable/selector_checkbox"

    android:clickable="false"
    android:focusable="false" />

 

summary:控件的摘要(说明)文字

entries:在单选/多选列表中,显示的选项信息。功能:给用户看

entryValues:在单选/多选列表中,选项信息对应的值。系统最后户把这个控件的key和这个值,以键值对的形式进行存放。

 <string-array name="floatColor">
        <item>白</item>
        <item>蓝</item>
        <item>灰</item>
        <item>胖丁</item>
        <item>布丁</item>
        <item>皮卡丘</item>
    </string-array>
    
    <string-array name="floatColor_value">
        <item>white</item>
        <item>blue</item>
        <item>gray</item>
        <item>pangding</item>
        <item>buding</item>
        <item>pikaqiu</item>
    </string-array>

第一个模块的全部布局
    <PreferenceCategory
        android:layout="@layout/prefs_category_widget"
        android:title="选择模块组" >

        <!-- 开关:定义了自定义的widget -->
        <CheckBoxPreference
            android:defaultValue="true"
            android:icon="@drawable/appstore"
            android:key="checkbox_preference"
            android:layout="@layout/prefs_list_s_item"
            android:title="开关"
            android:widgetLayout="@layout/checkbox_prefs_widget" />

        <!-- 单选:弹出一个单选list,选中的值会出现在摘要中 -->
        <com.kale.preference.MyListPreference
            android:dialogTitle="dialog_title_list_preference"
            android:entries="@array/floatColor"
            android:entryValues="@array/floatColor_value"
            android:icon="@drawable/itunes"
            android:key="list_preference"
            android:layout="@layout/prefs_list_s_item"
            android:summary="用单选列表来选择数据"
            android:title="单选列表" />
        <!-- 多选:弹出多选列表,选中的项目会出现在摘要中。这里的摘要由代码控制,所以请不要书写 -->
        <com.kale.preference.MyMultiSelectListPreference
            android:entries="@array/floatColor"
            android:entryValues="@array/floatColor_value"
            android:key="mult_preference"
            android:layout="@layout/prefs_list_s_item"
            android:title="多选列表" />
    </PreferenceCategory>

这里面用到了我自定的两个类,一个是单选菜单,一个是多选菜单。其实我就是继承了原本的类,然后让原本的summary中显示我们选中的值而已。下面是两个类的代码,很简单。

MyListPreference

package com.kale.preference;

import android.content.Context;
import android.preference.ListPreference;
import android.util.AttributeSet;
import android.view.View;

public class MyListPreference extends ListPreference{

    public MyListPreference(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyListPreference(Context context) {
        this(context, null);
    }
    
    @Override
    protected void onBindView(View view) {
        super.onBindView(view);
        setSummary(getEntry() == null?getSummary():getEntry());
        //setSummary(getEntry());
    }
}

 

MyMultiSelectListPreference

package com.kale.preference;

import java.util.Set;

import android.content.Context;
import android.preference.MultiSelectListPreference;
import android.util.AttributeSet;
import android.view.View;

public class MyMultiSelectListPreference extends MultiSelectListPreference {

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

    public MyMultiSelectListPreference(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * @return 列表选中状态的数组
     */
    private boolean[] getSelectedItems() {
        final CharSequence[] entries = getEntryValues();
        final int entryCount = entries.length;
        final Set<String> values = getValues();
        boolean[] result = new boolean[entryCount];

        for (int i = 0; i < entryCount; i++) {
            result[i] = values.contains(entries[i].toString());
        }
        return result;
    }

    private void updateSummary() {
        setSummary(" ");
        CharSequence[] c = getEntries();// 得到列表值数组
        String str = (String) getSummary();// 得到列表选择状态数组
        for (int i = 0; i < getSelectedItems().length; i++) {
            if (getSelectedItems()[i]) {
                str += (String) c[i] + ",";
            }
        }
        str = str.substring(0, str.length() - 1);// 去除最后一个逗号
        // 设置摘要的内容
        setSummary(str);
    }

    @Override
    protected void onBindView(View view) {
        super.onBindView(view);
        int i;
        for (i = 0; i < getSelectedItems().length; i++) {
            if (getSelectedItems()[i] == true) {
                updateSummary();
                break;
            }
        }
        if (i == getSelectedItems().length) {
            setSummary("");
        }
    }

    @Override
    protected void onDialogClosed(boolean positiveResult) {
        super.onDialogClosed(positiveResult);
        int i;
        for (i = 0; i < getSelectedItems().length; i++) {
            if (getSelectedItems()[i] == true) {
                updateSummary();
                break;
            }
        }
        if (i == getSelectedItems().length) {
            setSummary("");
        }
    }
}

 

于是我们第一个模块组就大功告成了!

 

--------------------------------------------------------------------------------------------

 

二、创建模块二(特殊模块组)

 

 

这个模块中有两个新的属性

negativeButtonText:设置对话框中取消按钮的文字

positiveButtonText:设置对话框中确定按钮的文字,点击确定后会将值保存下来

通过前面的铺垫,我们现在可以直接看懂以下的代码了

 <PreferenceCategory
        android:layout="@layout/prefs_category_widget"
        android:title="特殊模块组" >

        <!-- 输入框模块:输入的文字出现在摘要中 -->
        <com.kale.preference.MyEditTextPreference
            android:dialogIcon="@drawable/itunes"
            android:dialogTitle="请输入要保存的文字"
            android:key="edittext_preference"
            android:layout="@layout/prefs_list_s_item"
            android:negativeButtonText="cancel"
            android:positiveButtonText="ok"
            android:summary="点击输入文字"
            android:title="文本输入" />
        <!-- 滑动条模块:点击后出现滑动条对话框 -->
        <com.kale.preference.MySeekBarDialogPreference
            android:dialogLayout="@layout/seekbar_dialog_prefs_widget"
            android:dialogTitle="滑动来选择数值"
            android:key="seekbar_dialog_preference"
            android:layout="@layout/prefs_list_s_item"
            android:negativeButtonText="cancel"
            android:positiveButtonText="ok"
            android:summary="点击选择大小"
            android:title="滑动选择" />

        <com.kale.preference.MySeekBarPreference
            android:key="seekbar_preference"
            android:layout="@layout/prefs_list_s_item"
            android:summary="点击选择大小"
            android:title="滑动条"
            android:widgetLayout="@layout/arrow_widget" />
    </PreferenceCategory>

 

MyEditTextPreference,MySeekBarDialogPreference,MySeekBarPreference都是我自己定义的类,功能还是将所输入的/所选择的值显示到summary中。

MyEditTextPreference

package com.kale.preference;

import android.content.Context;
import android.preference.EditTextPreference;
import android.util.AttributeSet;
import android.view.View;

public class MyEditTextPreference extends EditTextPreference {

    public MyEditTextPreference(Context context, AttributeSet attrs,
            int defStyle) {
        super(context, attrs, defStyle);
    }

    public MyEditTextPreference(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onBindView(View view) {
        super.onBindView(view);
        setSummary(getText() == null?getSummary():getText());
    }

}

 

MySeekBarDialogPreference

/*
 * Copyright (C) 2007 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.
 */
package com.kale.preference;

import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.drawable.Drawable;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.SeekBar.OnSeekBarChangeListener;

import com.kale.shared.R;

public class MySeekBarDialogPreference extends DialogPreference implements
        OnSeekBarChangeListener {
    //private static final String TAG = "SeekBarDialogPreference";

    private Drawable mMyIcon;
    private TextView value;
    private SeekBar seekBar;
    // 如果要修改最大值和最小值的话,那么可以在这里修改。或者是调用函数setMax、setMin
    private int mMax = 100, mMin = 0;
    private int mProgress;

    public MySeekBarDialogPreference(Context context, AttributeSet attrs) {
        super(context, attrs);

        setDialogLayoutResource(R.layout.seekbar_dialog_prefs_widget);

        // Steal the XML dialogIcon attribute's value
        mMyIcon = getDialogIcon();
        setDialogIcon(null);
    }

    public MySeekBarDialogPreference(Context context) {
        this(context, null);
    }

    public void setMax(int max) {
        if (max != mMax) {
            mMax = max;
            notifyChanged();
        }
    }

    public void setMin(int min) {
        if (min != mMin) {
            mMin = min;
            notifyChanged();
        }
    }

    /**
     * Saves the progress to the {@link SharedPreferences}.
     * 
     * @param text
     *            The progress to save
     */
    public void setProgress(int progress) {
        final boolean wasBlocking = shouldDisableDependents();
        mProgress = progress;
        persistInt(mProgress);
        final boolean isBlocking = shouldDisableDependents();
        if (isBlocking != wasBlocking) {
            notifyDependencyChange(isBlocking);
        }
    }

    /**
     * Gets the text from the {@link SharedPreferences}.
     * 
     * @return The current preference value.
     */
    public int getProgress() {
        return mProgress;
    }

    @Override
    protected void onBindView(View view) {
        super.onBindView(view);
        mProgress = getPersistedInt(getProgress());
        setSummary(String.valueOf(mProgress));
    }

    @Override
    protected void onBindDialogView(View view) {
        super.onBindDialogView(view);
        final ImageView iconView = (ImageView) view.findViewById(android.R.id.icon);
        value = (TextView)view.findViewById(R.id.value);
        if (mMyIcon != null) {
            iconView.setImageDrawable(mMyIcon);
        } else {
            iconView.setVisibility(View.GONE);
        }
        seekBar = getSeekBar(view);
        seekBar.setMax(mMax-mMin);
        seekBar.setProgress(mProgress);
        seekBar.setOnSeekBarChangeListener(this);
        value.setText(String.valueOf(mProgress));
    }

    @Override
    protected void onDialogClosed(boolean positiveResult) {
        super.onDialogClosed(positiveResult);
        // 如果没按下确定按钮就返回null
        if (!positiveResult) {
            return;
        }
        if (shouldPersist()) {
            persistInt(mProgress);
            setSummary(String.valueOf(mProgress));
        }
        // 提交数据
        notifyChanged();
    }

    protected static SeekBar getSeekBar(View dialogView) {
        return (SeekBar) dialogView.findViewById(R.id.seekbar);
    }

    @Override
    public CharSequence getSummary() {
        String summary = super.getSummary().toString();
        int value = getPersistedInt(mProgress);
        return String.format(summary, value);
    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress,
            boolean fromUser) {
        value.setText(String.valueOf(progress));

    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
        // TODO 自动生成的方法存根
    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
        // TODO 自动生成的方法存根
        mProgress = seekBar.getProgress() + mMin;
    }
}

 

用到的布局文件:seekbar_dialog_prefs_widget.xml

 

<?xml version="1.0" encoding="UTF-8"?>
<!--
     Copyright (C) 2008 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.

-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical" >

    <ImageView
        android:id="@android:id/icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingTop="20dp" />

    <TextView
        android:id="@+id/value"
        android:paddingTop="20dp" 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:text="value"
        android:textSize="20sp"
        android:textColor="#aaaaaa" />

    <SeekBar
        android:id="@+id/seekbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="20dp" />

</LinearLayout>

 

MySeekBarPreference

/*
 * Copyright (C) 2011 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.
 */
package com.kale.preference;

import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import android.preference.Preference;
import android.util.AttributeSet;
import android.view.View;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;

import com.kale.shared.R;


public class MySeekBarPreference extends Preference implements OnSeekBarChangeListener {
    private TextView value;
    private int mProgress;
    private int mMax = 100;
    private boolean mTrackingTouch;
    
    public MySeekBarPreference(
            Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        setMax(mMax);
        setLayoutResource(R.layout.seekbar_prefs);
    }

    public MySeekBarPreference(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MySeekBarPreference(Context context) {
        this(context, null);
    }
    
    @Override
    protected void onBindView(View view) {
        super.onBindView(view);
        SeekBar seekBar = (SeekBar) view.findViewById(R.id.seekbar);
        seekBar.setMax(mMax);
        seekBar.setProgress(mProgress);
        seekBar.setEnabled(isEnabled());
        seekBar.setOnSeekBarChangeListener(this);
        value = (TextView)view.findViewById(R.id.value);
        value.setText(String.valueOf(mProgress));
    }

    @Override
    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
        setProgress(restoreValue ? getPersistedInt(mProgress): (Integer) defaultValue);
    }

    public void setMax(int max) {
        if (max != mMax) {
            mMax = max;
            notifyChanged();
        }
    }

    public void setProgress(int progress) {
        setProgress(progress, true);
    }

    private void setProgress(int progress, boolean notifyChanged) {
        if (progress > mMax) {
            progress = mMax;
        }
        if (progress < 0) {
            progress = 0;
        }
        if (progress != mProgress) {
            mProgress = progress;
            persistInt(progress);
            if (notifyChanged) {
                notifyChanged();
            }
        }
    }

    public int getProgress() {
        return mProgress;
    }

    /**
     * Persist the seekBar's progress value if callChangeListener
     * returns true, otherwise set the seekBar's progress to the stored value
     */
    void syncProgress(SeekBar seekBar) {
        int progress = seekBar.getProgress();
        if (progress != mProgress) {
            if (callChangeListener(progress)) {
                setProgress(progress, false);
            } else {
                seekBar.setProgress(mProgress);
            }
        }
    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        
        if (fromUser) {
            System.err.println("now value = "+progress);
            
        }
        if (fromUser && !mTrackingTouch) {
            syncProgress(seekBar);
        }
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
        mTrackingTouch = true;
    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
        mTrackingTouch = false;
        if (seekBar.getProgress() != mProgress) {
            syncProgress(seekBar);
            value.setText(seekBar.getProgress()+"");
        }
    }

    @Override
    protected Parcelable onSaveInstanceState() {
        /*
         * Suppose a client uses this preference type without persisting. We
         * must save the instance state so it is able to, for example, survive
         * orientation changes.
         */

        final Parcelable superState = super.onSaveInstanceState();
        if (isPersistent()) {
            // No need to save instance state since it's persistent
            return superState;
        }

        // Save the instance state
        final SavedState myState = new SavedState(superState);
        myState.progress = mProgress;
        myState.max = mMax;
        return myState;
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        if (!state.getClass().equals(SavedState.class)) {
            // Didn't save state for us in onSaveInstanceState
            super.onRestoreInstanceState(state);
            return;
        }

        // Restore the instance state
        SavedState myState = (SavedState) state;
        super.onRestoreInstanceState(myState.getSuperState());
        mProgress = myState.progress;
        mMax = myState.max;
        notifyChanged();
    }

    /**
     * SavedState, a subclass of {@link BaseSavedState}, will store the state
     * of MyPreference, a subclass of Preference.
     * <p>
     * It is important to always call through to super methods.
     */
    private static class SavedState extends BaseSavedState {
        int progress;
        int max;

        public SavedState(Parcel source) {
            super(source);

            // Restore the click counter
            progress = source.readInt();
            max = source.readInt();
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            super.writeToParcel(dest, flags);
            // Save the click counter
            dest.writeInt(progress);
            dest.writeInt(max);
        }

        public SavedState(Parcelable superState) {
            super(superState);
        }

    }
}

所用到的布局文件:seekbar_prefs.xml

<?xml version="1.0" encoding="utf-8"?>
<!--
     Copyright (C) 2011 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.

-->


<!--
     Layout for a Preference in a PreferenceActivity. The
     Preference is able to place a specific widget for its particular
     type in the "widget_frame" layout.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:baselineAligned="false"
    android:gravity="center_vertical"
    android:background="@drawable/selector_item"
    android:minHeight="50dp"
    android:paddingLeft="16dp" >

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:gravity="center"
        android:minWidth="0dp"
        android:orientation="horizontal" >

        <ImageView
            android:id="@+android:id/icon"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_gravity="center"
            android:layout_marginRight="6dp"
            android:scaleType="fitCenter"
            android:src="@drawable/appstore" />

        <TextView
            android:id="@+android:id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:ellipsize="marquee"
            android:fadingEdge="horizontal"
            android:singleLine="true"
            android:text="title"
            android:textAppearance="?android:attr/textAppearanceMedium" />
    </LinearLayout>

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="3dip"
        android:layout_marginLeft="16dip"
        android:layout_marginRight="6dip"
        android:layout_marginTop="3dip"
        android:layout_weight="1" >

        <!-- 注意:一定得设置进度条的高度,不然进度条会很高。  -->

        <TextView
            android:id="@+id/value"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_centerHorizontal="true"
            android:maxLines="1"
            android:text="value"
            android:textAppearance="?android:attr/textAppearanceSmall"
            android:textColor="#aaaaaa" />

        <SeekBar
            android:id="@+id/seekbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:maxHeight="3dp"
            android:minHeight="1dp"
            android:layout_below="@id/value"
            android:progressDrawable="@drawable/layer_list_progress"
            android:thumb="@drawable/thumb" />

    </RelativeLayout>

</LinearLayout>

于是,我们就完成了第二个模块的设置。

 

--------------------------------------------------------------------------------------------

 

三、第三个模块组的(触发模块组)

这个组里面有一个点击触发动作和自定义preference

<PreferenceCategory android:layout="@layout/prefs_category_widget" >
        <PreferenceScreen
            android:icon="@drawable/ic_launcher"
            android:layout="@layout/prefs_list_s_item"
            android:title="点击触发Intent动作" >
            <intent
                android:action="android.intent.action.VIEW"
                android:data="http://www.android.com" />
        </PreferenceScreen>

        <Preference
            android:key="my_prefs_key"
            android:layout="@layout/prefs_list_s_item"
            android:title="自定义Preference" >
        </Preference>
    </PreferenceCategory>

 

intent模块中我们设置一个action和data属性,这样点击它后,与之对应的Activity将被启动。这里实现的效果是启动浏览器来展示网页。

        <activity android:name="com.kale.shared.SecondLevelActivity" >
            <intent-filter>
                <action android:name="com.kale.intent.action.MY_ACTION" />

                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>

 

我们还自定义了一个preference,用来做一些特殊的操作。这个preference不用写什么自定义类,直接在这个的Activity中找到它既可。

package com.kale.shared;

import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.widget.Toast;

public class MainActivity extends PreferenceActivity implements
        OnPreferenceClickListener {
    /** 自定义布局 **/
    Preference myPrefers = null;

    @SuppressWarnings("deprecation")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 设置背景图,给activity设置后。所有fragment的背景都会改了,十分方便!
        // getWindow().setBackgroundDrawable(getResources().getDrawable(R.color.bgColor));
        // setContentView(R.layout.activity_main); 这里就不能设置布局了

        setContentView(R.layout.prefs_list_content);
        addPreferencesFromResource(R.xml.customer_prefs);
        // 初始化控件
        myPrefers = findPreference("my_prefs_key");

        myPrefers.setSummary("可以自定义布局和点击事件");
        myPrefers.setOnPreferenceClickListener(this);
    }

    @Override
    public boolean onPreferenceClick(Preference preference) {
        if (preference == myPrefers) {
            Toast.makeText(MainActivity.this, "自定义Preference被按下",
                    Toast.LENGTH_SHORT).show();
        } 
        return false;
    }
    
    
}

 

现在,这个模块也定义好了。

 

--------------------------------------------------------------------------------------------

四、子父依赖模块组

 

这个模块存在一个依赖关系,子控件必须在父控件开启的时候才能进行操作,否则不可用。也就是说只有上面的父控件的开关开启,下面的子控件的开关才能用。

这里的关键属性是:android:dependency="parent_checkbox_preference",设置这个属性的控件表明自己是依托于parent_checkbox_preference这个控件的

  <PreferenceCategory
        android:layout="@layout/prefs_category_widget"
        android:title="子父依赖模块组" >
        <CheckBoxPreference
            android:icon="@drawable/camera"
            android:key="parent_checkbox_preference"
            android:layout="@layout/prefs_list_s_item"
            android:title="父选择控件"
            android:widgetLayout="@layout/checkbox_prefs_widget" />

        <!-- The visual style of a child is defined by this styled theme attribute. -->
        <!-- 子控件关联父控件,在父控件选中后子控件才可用 -->
        <!-- android:layout="?android:attr/preferenceLayoutChild" -->
        <CheckBoxPreference
            android:dependency="parent_checkbox_preference"
            android:icon="@drawable/calculator"
            android:key="child_checkbox_preference"
            android:layout="@layout/prefs_list_s_item"
            android:title="子控件(依托于父控件)"
            android:widgetLayout="@layout/checkbox_prefs_widget" />
    </PreferenceCategory>

 

--------------------------------------------------------------------------------------------

 

五、二级菜单模块组

点击任何一个栏目,就会跳到另一个Activity中进行操作,最后系统会将选择的值返回到这个Activity中。

<PreferenceCategory
        android:layout="@layout/prefs_category_widget"
        android:title="二级菜单模块组" >
        <PreferenceScreen
            android:layout="@layout/prefs_list_s_item"
            android:summary="点击进入二级菜单"
            android:title="传统方式的二级菜单" >
            <PreferenceCategory android:layout="@layout/prefs_category_widget" >
                <CheckBoxPreference
                    android:defaultValue="true"
                    android:key="cb21"
                    android:layout="@layout/prefs_list_item"
                    android:summaryOff="关闭"
                    android:summaryOn="开启"
                    android:title="功能1" />

                <SwitchPreference
                    android:defaultValue="true"
                    android:dialogTitle="点击后触发的功能"
                    android:key="autoScroll"
                    android:layout="@layout/prefs_list_item"
                    android:summaryOff="关闭"
                    android:summaryOn="开启"
                    android:title="自动滚动" />

                <ListPreference
                    android:dialogTitle="请选择"
                    android:entries="@array/onTouch"
                    android:entryValues="@array/onTouch_value"
                    android:key="list1"
                    android:layout="@layout/prefs_list_item"
                    android:summary="单选框"
                    android:title="android forum" />

                <EditTextPreference
                    android:defaultValue="Hello EditTextPreference"
                    android:dialogTitle="输入设置"
                    android:key="et1"
                    android:layout="@layout/prefs_list_item"
                    android:summary="点击输入"
                    android:title="EditTextPreference Sample" />
            </PreferenceCategory>
        </PreferenceScreen>
        <PreferenceScreen
            android:layout="@layout/prefs_list_s_item"
            android:title="启动一个Activity作二级菜单"
            android:widgetLayout="@layout/arrow_widget" >
            <intent android:action="com.kale.intent.action.MY_ACTION" />
        </PreferenceScreen>

        <Preference
            android:key="getValue_single_prefers"
            android:layout="@layout/prefs_list_s_item"
            android:summary="这是选择的值"
            android:title="有返回值的单选菜单" >
        </Preference>
        <Preference
            android:key="getValue_multi_prefers"
            android:layout="@layout/prefs_list_s_item"
            android:summary="这是选择的值"
            android:title="有返回值的多选菜单" >
        </Preference>
    </PreferenceCategory>

 

下面这种嵌套产生二级菜单的方式是系统给出的,很简单,但是扩展性很差。↓

    <PreferenceScreen
            android:layout="@layout/prefs_list_s_item"
            android:summary="点击进入二级菜单"
            android:title="传统方式的二级菜单" >
            <PreferenceCategory android:layout="@layout/prefs_category_widget" >
                <CheckBoxPreference
                    android:defaultValue="true"
                    android:key="cb21"
                    android:layout="@layout/prefs_list_item"
                    android:summaryOff="关闭"
                    android:summaryOn="开启"
                    android:title="功能1" />

                <SwitchPreference
                    android:defaultValue="true"
                    android:dialogTitle="点击后触发的功能"
                    android:key="autoScroll"
                    android:layout="@layout/prefs_list_item"
                    android:summaryOff="关闭"
                    android:summaryOn="开启"
                    android:title="自动滚动" />

                <ListPreference
                    android:dialogTitle="请选择"
                    android:entries="@array/onTouch"
                    android:entryValues="@array/onTouch_value"
                    android:key="list1"
                    android:layout="@layout/prefs_list_item"
                    android:summary="单选框"
                    android:title="android forum" />

                <EditTextPreference
                    android:defaultValue="Hello EditTextPreference"
                    android:dialogTitle="输入设置"
                    android:key="et1"
                    android:layout="@layout/prefs_list_item"
                    android:summary="点击输入"
                    android:title="EditTextPreference Sample" />
            </PreferenceCategory>
        </PreferenceScreen>

 

下面这个就是一个intent,点击后跳转到指定的Activity中去。是intent的隐式跳转。↓

     <PreferenceScreen
            android:layout="@layout/prefs_list_s_item"
            android:title="启动一个Activity作二级菜单"
            android:widgetLayout="@layout/arrow_widget" >
            <intent android:action="com.kale.intent.action.MY_ACTION" />
        </PreferenceScreen>

我定义了这样一个Activity来相应这个intent。

需要注意的是:这里面category节点必须定义,否则会出现找不到action的情况!

        <activity android:name="com.kale.shared.SecondLevelActivity" >
            <intent-filter>
                <action android:name="com.kale.intent.action.MY_ACTION" />

                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>

下面这种其实就是自定义了preference,点击后跳转到一个Activity中去。↓

      <Preference
            android:key="getValue_single_prefers"
            android:layout="@layout/prefs_list_s_item"
            android:summary="这是选择的值"
            android:title="有返回值的单选菜单" >
        </Preference>
        <Preference
            android:key="getValue_multi_prefers"
            android:layout="@layout/prefs_list_s_item"
            android:summary="这是选择的值"
            android:title="有返回值的多选菜单" >
        </Preference>

MainActivity

package com.kale.shared;

import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.widget.Toast;

public class MainActivity extends PreferenceActivity implements
        OnPreferenceClickListener {
    /** 自定义布局 **/
    Preference myPrefers = null;
    Preference getValueSingPrefs,getValueMultiPrefs;
    SharedPreferences sp;
    SharedPreferences.Editor editor;

    @SuppressWarnings("deprecation")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 设置背景图,给activity设置后。所有fragment的背景都会改了,十分方便!
        // getWindow().setBackgroundDrawable(getResources().getDrawable(R.color.bgColor));
        // setContentView(R.layout.activity_main); 这里就不能设置布局了

        sp = getSharedPreferences("kaleShared", MODE_PRIVATE);
        editor = sp.edit();
        editor.putString("KEY", "value");
        editor.commit();

        if (sp.contains("KEY")) {
            System.out.println("have a key");
        }
        setContentView(R.layout.prefs_list_content);
        addPreferencesFromResource(R.xml.customer_prefs);
        // 初始化控件
        initPrefers();

        myPrefers.setSummary("可以自定义布局和点击事件");
        myPrefers.setOnPreferenceClickListener(this);
        getValueSingPrefs.setOnPreferenceClickListener(this);
        getValueMultiPrefs.setOnPreferenceClickListener(this);
    }

    @Override
    public boolean onPreferenceClick(Preference preference) {
        if (preference == myPrefers) {
            Toast.makeText(MainActivity.this, "自定义Preference被按下",
                    Toast.LENGTH_SHORT).show();
        } else if (preference == getValueSingPrefs) {
            Intent intent = new Intent(MainActivity.this, SingleActivity.class);
            startActivityForResult(intent, 100);
        }
        else if (preference == getValueMultiPrefs) {
            Intent intent = new Intent(MainActivity.this, MultiActivity.class);
            startActivityForResult(intent, 120);
        }
        return false;
    }
    
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        //如果返回码表示RESULT_OK,那么说明是从SecondActivity返回的intent
        if (resultCode == RESULT_OK) {
            //如果是100表示是返回给启动activity的intent(这里是我们的getValuePreference)的值
            switch (requestCode) {
            case 100:
                getValueSingPrefs.setSummary(data.getExtras().getString("osNameKey"));
                break;
            case 120:
                getValueMultiPrefs.setSummary(data.getExtras().getString("languageKey"));
                break;
            default:
                break;
            }
                
        }
    }
    
    @SuppressWarnings("deprecation")
    private void initPrefers() {
        myPrefers = findPreference("my_prefs_key");
        getValueSingPrefs = findPreference("getValue_single_prefers");
        getValueMultiPrefs = findPreference("getValue_multi_prefers");
    }
}

 

跳转的界面如下:

  上面是单选列表,下面是多选列表。我已经写好了一个类,大家只需要找到这个activity中的所有checkboxpreference,然后通过一个方法就能设置为单选组和多选组了。很简单吧~

//添加到单选列表中
addToSingleChoiceList(android,ios,wp);
// 添加到多选列表中
addToMultiChoiceList("languageKey",java, c, js);

package com.kale.shared;

import android.os.Bundle;
import android.preference.CheckBoxPreference;

import com.kale.preference.ChoicePrefsActivity;

/**
 * @author:Jack Tony
 * @tips  :展现多选列表的界面
 * 初始化后将preference添加到多选列表中即可
 * addToSingleChoiceList(android,ios,wp);
 * @date  :2014-8-6
 */
@SuppressWarnings("deprecation")
public class MultiActivity extends ChoicePrefsActivity {
    CheckBoxPreference android,ios,wp;
    CheckBoxPreference java, c, js;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().setBackgroundDrawableResource(R.color.bgColor);
        setContentView(R.layout.prefs_list_content);
        addPreferencesFromResource(R.xml.second_level_prefs);

        initPreference();
        //添加到单选列表中
        addToSingleChoiceList(android,ios,wp);
        // 添加到多选列表中
        addToMultiChoiceList("languageKey",java, c, js);
    }

    private void initPreference() {
        android = (CheckBoxPreference) findPreference("android_prefs");
        ios = (CheckBoxPreference) findPreference("ios_prefs");
        wp = (CheckBoxPreference) findPreference("wp_prefs");
        
        java = (CheckBoxPreference) findPreference("java_prefs");
        c = (CheckBoxPreference) findPreference("c_prefs");
        js = (CheckBoxPreference) findPreference("js_prefs");
        
    }

}

 

 
 

在MainActivity中我用onActivityResult()取得在二级菜单中选中的值,并且赋值给summary。

package com.kale.shared;

import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.widget.Toast;

public class MainActivity extends PreferenceActivity implements
        OnPreferenceClickListener {
    /** 自定义布局 **/
    Preference myPrefers = null;
    Preference getValueSingPrefs,getValueMultiPrefs;
    SharedPreferences sp;
    SharedPreferences.Editor editor;

    @SuppressWarnings("deprecation")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 设置背景图,给activity设置后。所有fragment的背景都会改了,十分方便!
        // getWindow().setBackgroundDrawable(getResources().getDrawable(R.color.bgColor));
        // setContentView(R.layout.activity_main); 这里就不能设置布局了

        sp = getSharedPreferences("kaleShared", MODE_PRIVATE);
        editor = sp.edit();
        editor.putString("KEY", "value");
        editor.commit();

        if (sp.contains("KEY")) {
            System.out.println("have a key");
        }
        setContentView(R.layout.prefs_list_content);
        addPreferencesFromResource(R.xml.customer_prefs);
        // 初始化控件
        initPrefers();

        myPrefers.setSummary("可以自定义布局和点击事件");
        myPrefers.setOnPreferenceClickListener(this);
        getValueSingPrefs.setOnPreferenceClickListener(this);
        getValueMultiPrefs.setOnPreferenceClickListener(this);
    }

    @Override
    public boolean onPreferenceClick(Preference preference) {
        if (preference == myPrefers) {
            Toast.makeText(MainActivity.this, "自定义Preference被按下",
                    Toast.LENGTH_SHORT).show();
        } else if (preference == getValueSingPrefs) {
            Intent intent = new Intent(MainActivity.this, SingleActivity.class);
            startActivityForResult(intent, 100);
        }
        else if (preference == getValueMultiPrefs) {
            Intent intent = new Intent(MainActivity.this, MultiActivity.class);
            startActivityForResult(intent, 120);
        }
        return false;
    }
    
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        //如果返回码表示RESULT_OK,那么说明是从SecondActivity返回的intent
        if (resultCode == RESULT_OK) {
            //如果是100表示是返回给启动activity的intent(这里是我们的getValuePreference)的值
            switch (requestCode) {
            case 100:
                getValueSingPrefs.setSummary(data.getExtras().getString("osNameKey"));
                break;
            case 120:
                getValueMultiPrefs.setSummary(data.getExtras().getString("languageKey"));
                break;
            default:
                break;
            }
                
        }
    }
    
    @SuppressWarnings("deprecation")
    private void initPrefers() {
        myPrefers = findPreference("my_prefs_key");
        getValueSingPrefs = findPreference("getValue_single_prefers");
        getValueMultiPrefs = findPreference("getValue_multi_prefers");
    }
}

 

好啦,主要的讲解就到这里,下面附上源码。

源码下载:http://download.youkuaiyun.com/detail/shark0017/7726083

 

基于Simulink的超级电容快速响应特性建模与仿真 本文介绍了基于Simulink的超级电容快速响应特性建模与仿真方法。主要内容包括:超级电容的工作原理、一阶RC等效电路模型及其动态电压方程;通过Simulink搭建超级电容模型的两种方法(Simscape Electrical推荐方案和基础模块透明教学方案);仿真100A×100ms放电脉冲下的电压响应特性,并与锂电池进行动态对比;分析超级电容在混合储能系统中的功率分配优势。文章还提供了工程实践要点和扩展方向,如电压均衡、温度影响评估以及与DC-DC变换器集成等。通过建模实践,验证了超级电容在毫秒级响应、高 阅读详情

相关推荐

OpenCode:开源代码智能代理平台的全面解析

OpenCode是一个开源代码智能代理平台,采用模块化微服务架构,支持多种AI模型集成。该系统包含前端交互层、代理服务层和模型集成层,提供Build、Plan和General三种代理模式,分别负责代码编写、分析和复杂任务处理。OpenCode支持跨平台安装,具备权限验证、上下文感知等安全机制,并集成LSP协议实现深度代码理解。相比竞品,其开源特性、终端优先设计和提供商无关架构具有明显优势,适用于个人开发、团队协作等多种场景,为开发者提供强大的AI辅助编程能力。

Aaron_945的博客 1423

Android--自定义SeekBarPreference控件

运行截图:       java代码:   package cn.mrzhu.test23; import android.os.Bundle; import android.preference.PreferenceActivity; /** * 继承PreferenceActivity,设置布局 * @author ZLQ * */ public class

zlQQhs(莫落)的专栏 4361

Unity3d实现红外热成像效果

1.将需要在红外图像中高亮的物体设置到图层PostProcessing。 2.新建一个相机CameraHighLight,设置其Culling Mask为PostProcessing,也就是在这个相机中只有PostProcessing图层的物体可见。 3.新建一个RenderTexture,命名为HightLightRt,将CameraHighLight的Target Texture设HightLightRt。 4.新建一个相机Camera2,这个相机用于显示其他物体。 接下来将CameraHigh

zouxin_88的专栏 8327

自定义PreferenceActivity和PreferenceFragment的样式

感谢:http://blog.csdn.net/luck_apple/article/details/7064004 这篇文章讲的是如何定义fragment的样式,基本布局都是从源码中弄过来的。通过设置布局文件的属性,让我们可以自定义preference的界面。 先来看看xml文件中的内容 &lt;PreferenceScreen xmlns:android="http://schema...

weixin_34219944的博客 278

Echats-wordcloud 文字云图的踩坑点【Unknown series wordCloud】

一直都是报 【ECharts Unknown series wordCloud】这玩意可能要刷脸,祝你好运吧~

Sky_Bug的博客 2418

【CubeMX】STM32G474 HRTIM 互补PWM与死区时间配置实战

本文详细介绍了如何使用STM32CubeMX配置STM32G474的高分辨率定时器HRTIM生成互补PWM波形并精确设置死区时间。通过实战步骤讲解时钟配置、死区计算与高级功能,帮助工程师在电机驱动和电源应用中实现精准控制,避免上下桥臂直通风险。

grpc6streamer的博客 727

[转]AndroidTips]修改preference中view属性

http://www.cnblogs.com/slider/archive/2011/11/10/2245017.html android开发包preference提供了我们一些方面的设置工具,PreferenceActivity,Preference,PreferenceScreen,CheckBoxPreference等,利用他们可以方便的建立应用程序的属性设置。如图:有些时候,我们并不需要系

大海蓝天的专栏 732

赛灵思Xilinx 7系列FPGA硬件设计知识总结

本文讲述了赛灵思7系列FPGA硬件设计的关键点,为使用该系列FPGA提供硬件设计参考。

csdn_dsw的博客 3618

PreferenceActivity的使用 程序动态添加修改Preference

PreferenceActivity的XML中主要有7种元素,分别是PreferenceScreen(根元素),CheckBoxPreference(CheckBox选项,如系统设置-无线网络里的飞行模式),EditTextPreference(弹出一个EditText),ListPreference(弹出一个选择列表),Preference(只显示文本,需要在代码中添加点击事件),Prefere

梦家之家 2835

15、虚拟环境中的数据保护与恢复方案解析

本文深入解析了虚拟环境中两种重要的数据保护与恢复解决方案:RecoverPoint for Virtual Machines(RP4VM)在VxRail上的设计,以及Veeam Backup & Replication(VBR)与VxRail的集成方案。文章详细介绍了RP4VM的网络布局、故障转移场景及其相关问题,以及VBR的架构设计、备份与复制流程、许可证选项及其多平台支持特性。此外,还探讨了RP4VM与VBR结合使用的综合解决方案,以及未来数据保护领域的发展趋势,如智能化管理、云原生集成和零信任架构,为

t4y5u6i7o的博客 95

STM32 USB键盘实现指南

在STM32上实现键盘功能可以通过USB HID(人机接口设备)协议来实现,使STM32设备能被计算机识别为标准键盘。通过上述步骤,您可以在STM32上实现一个功能完整的USB键盘。关键点包括:正确配置USB HID设备实现键盘矩阵扫描处理按键事件并生成HID报告优化按键响应和消抖处理添加额外功能如宏键和LED反馈这个实现可以作为基础,进一步扩展为游戏键盘、宏键盘或特殊输入设备。

suny8的专栏 968

PreferenceActivity && PreferenceFragment

从源码角度一步一步来修改PreferenceActivity界面 http://www.cnblogs.com/tianzhijiexian/p/3893504.html SwitchPreference 在 pre-L 下的 bug http://seven332.github.io/swi...

风言风语 103

【Zephyr存储专题】09_LittleFs文件系统操作

本文档旨在为开发者介绍Zephyr下Littlef's文件系统相关配置使用指南

zero的博客 3246

如何使PreferenceActivity中的EditTextPreference中summary显示当前的值

原始情况下EditTextPreference中summary是显示自己设置的一个固定值,就和titile一样,常用来做一些补充说明。 但很多时候,我们会想要让summary显示当前该key的值,类似下图 当我们更改对应值后,下面summary显示的值也会跟着更改。要实现这个功能,其实只需从OnSharedPreferenceChangeListener入手就可

daguaio_O的专栏 1万+

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

iOS 多语言本地化教程,VS Code KXApp 编辑 String Catalog .xcstrings

做 iOS 多语言本地化总要在 VS Code 和 Xcode 之间来回切?这篇讲用 KXApp(快蝎)在 VS Code 里直接编辑 String Catalog(.xcstrings):双击打开和 Xcode 一样的表格,`.xcstrings` 与老的 `.strings`、`.stringsdict` 同表编辑,加语言同步登记到工程、从源码扫描同步新 key,复数、设备变体和变量占位都在表里处理,改源语言文本后已有翻译自动标需复核。文件仍是标准格式,回 Xcode 打开一致。

2501_91592143的博客 163

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

成为全栈·基础补充·Nginx 入门:反向代理与静态托管

Nginx 可以托管前端静态文件,也可以作为反向代理把 HTTPS 请求转给内部 API。本文从请求路径讲起,解释反向代理、静态目录、代理头、路径拼接、可信 IP、Cookie、上传体积、超时和缓存边界,并提供配置结构示例和逐层排查顺序。配置片段需要结合实际证书、路由和运行平台验证,不能直接复制为生产方案。

FungLeo's Blog 290

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

Android 开发入门实践(二):Kotlin 空安全,从可空类型到安全访问

延续 NewsApp 项目中的新闻标题练习,记录 Kotlin 安全调用。

Easyhood的博客 277

Android Message源码阅读

本文梳理了Android Handler机制中Message的核心设计:链表结构、消息复用池(sPool)及回收机制,重点解析了异步消息与同步屏障(SyncBarrier)的协同机制。当主线程需优先处理UI绘制等高优先级任务时,系统通过postSyncBarrier插入屏障,使Looper跳过普通同步消息,优先执行标记为异步的UI渲染任务,确保帧率流畅。该机制是保障Android界面响应性的关键技术之一。

u012116089的博客 189

Android 面试题逐题简析:28 个专题,232 题

常见原因是未在 onMeasure 处理 AT_MOST 下的期望尺寸,沿用了默认测量而占满可用区域,或未考虑内容、padding 和最小尺寸。应计算 desiredWidth/Height,再通过 resolveSizeAndState 等与父约束合并。wrap_content 是测量输入,框架不会自动知道自定义内容想要多大。

一个踩这七色彩虹的路人甲 151

指纹浏览器手机版能用吗?区分原生 App、移动网页模拟与云手机

本文说明“指纹浏览器手机版”可能指手机原生 App、远程云端浏览器、电脑模拟手机网页或云手机。通过对比运行位置与可完成的任务,提供下载前的四项核对方法,帮助读者选择适合网页操作或 Android App 的方案。

2601_96176203的博客 135

云手机私有化部署完整方案:基于ARM服务器搭建私有安卓云集群

ARM 服务器是私有化安卓云集群最优硬件底座,原生架构匹配 Android,高密度、低损耗,非常适合企业内网场景。整套私有化方案的核心,从来不是 “跑起来安卓”,而是隔离可控、可审计、可运维、可扩容。两条路线各有取舍:开源自建灵活、门槛高;商用私有化交付开箱即用、安全能力完备,是当前企业生产环境主流方案。六边云手机私有化方案,补齐了自建方案在驱动适配、管控平台、审计合规、信创适配上的短板,适合对数据主权、内网隔离有强需求的政企、自研 App、垂直行业客户。

a598652的博客 308

Android 开发入门实践(九):把新闻数据与页面状态交给 ViewModel

本文记录第110—117课学习成果:将页面标题、新闻列表及选中编号逐步迁移至NewsViewModel,通过ViewModelProvider获取实例并读取数据。页面调用ViewModel方法处理选择与返回,实现状态管理解耦。过程中纠正了对Activity重建机制的误解,并厘清mutableStateOf的可观察性与ViewModel对象生命周期的区别,深化了对Compose状态更新机制的理解。

Easyhood的博客 212

Android 17 全栈性能分析关键字与源码手册

FrameTimeline 找异常帧,ATrace 找阶段,thread_state 找等待类型,ftrace 找内核原因,Simpleperf 找 CPU 热点,驱动源码找最终触发条件;任何根因结论都必须回到同一对象、同一时间窗口和同一条依赖链。

weixin_43228946的博客 363

智叟云集-手机证据抓取系统的工程结构与应用入口

本文系统阐述手机证据抓取系统的工程结构与应用入口,涵盖四大层面:工程目录划分(Android、Python服务、Web页面、测试与构建工具);Android组件通过Manifest声明可启动页面,主界面由MainActivity管理;Python服务通过run.py解析参数并启动FastAPI应用,支持远程访问;浏览器页面采用HTML/CSS/JS分离架构,静态资源通过路径关联,实现动态交互。各模块分工明确,协同构建可运行的采集系统。

2301_77178996的博客 230

Android 系统启动与调试中的日志抓取方式全解析

│ 用户操作/系统事件 ││ │▼ ▼│ (驱动/硬件/内存管理) │ │ (App/SystemServer) ││ │ │ ││ │▼ ▼│ dmesg 命令 │ │ logd daemon ││ cat /proc/kmsg │ │ (分类存储到分区) ││ │▼ ▼│ 串口 UART │ │ logcat 命令 ││ (硬件直连,最早输出) │ │ adb logcat ││ │▼│ (汇总所有日志+系统状态) │工具最佳用途关键优势主要局限dmesg。

zhaoshuzhaoshu的专栏 260

【Android】听写播放器改版:中英文书写速度拆项与「正在读」高亮

现象:把播放页主体抽成后,里面给LazyColumn加想让它占满剩余高度,编译报错(weight 不可用)。原因weight不是通用修饰符,是RowScope的扩展函数。是个普通函数,函数体里没有接收者,自然调不到。解决private fun ColumnScope.PlaybackBody(...) { /* 这里就能用 Modifier.weight(1f) */ }调用处仍在原来的Column { }里,无需改动。预防:Compose 的布局修饰符都是作用域绑定。

木头大左的博客 220
上一篇: ServletFileUpload使用方法
下一篇: mysql 递归查询
weixin_34090643
博客等级 码龄11年 3671粉丝 180原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值