Android: 如何在PreferenceActivity中启动自定义偏好设置界面

10
我想要从我的PreferenceActivity启动第二个Preference屏幕。并且在第二个Preference屏幕中,我想要使用预定义的xml布局。所以我有两个问题:
1. 如何将xml布局用作首选项的布局视图? 2. 如何将此自定义首选项添加到PreferenceActivity中,以便在点击时启动?
谢谢
*编辑以回应alibi
我试图从首选项屏幕中声明要启动的活动启动一个活动,这会导致以下异常:
 04-01 19:04:37.962: ERROR/AndroidRuntime(8061): android.content.ActivityNotFoundException: Unable to find explicit activity class {com.me/CustomPrefScreen}; have you declared this activity in your AndroidManifest.xml?

*另一个更新。 但是,如果我在settings.xml中将PrefrenceScreen替换为Preference的某个扩展,该扩展覆盖了onClick()以启动CustomPrefScreen,则一切正常。

主要偏好设置活动:

public class MyPreferences extends PreferenceActivity 
{
    @Override
    protected void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.settings);
    }
}

settings.xml

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <PreferenceScreen  
        android:summary="my summary" 
        android:title="my title">
        <intent android:action="android.intent.action.MAIN"
                    android:targetPackage="com.me"
                    android:targetClass="CustomPrefScreen"/>
    </PreferenceScreen>

</PreferenceScreen>

清单文件

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.me"
    android:versionCode="1"
    android:versionName="1.0">
    <application 
        android:icon="@drawable/icon" 
        android:label="@string/app_name" 
        android:theme="@style/Theme.NoBackground">
        <activity 
            android:name=".MyApp"
            android:label="@string/app_name">
            <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
             </activity>
        <activity 
            android:name=".CustomPrefScreen"
            android:label="@string/app_name">
        </activity>
        <activity 
            android:name=".MyPreferences"
            android:label="@string/app_name">
        </activity>
    </application>
    <uses-sdk android:minSdkVersion="4" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest> 

1
这两个东西在Api Demos示例中都有很好的解释。有趣的是它们在SDK目录中,因此你现在就可以在硬盘上找到这些示例。 - Cristian
你是否一定需要其他偏好设置成为第二个活动?如果你只是想要多个屏幕,也许只需使用PreferenceScreen即可。 - slund
@Slund。我想使用PreferenceScreen,但我不确定如何为其使用自定义xml布局;似乎只能添加Preference类型的视图? - ab11
啊哈,现在我明白问题了。您想要一个使用自定义UI而不是标准FooPreference视图的首选项屏幕。为此,您最有可能想要创建自己的MyPreference扩展Preference。 - slund
@Slund。嗯,这就是我想知道如何做的。我能为MyPreference定义布局吗?当它被点击时会启动吗?还是MyPreference必须启动一个使用我的自定义布局的新活动? - ab11
@Christian。我在Api Demos中找到了AdvancedPrefence示例。这仍然从首选项xml生成布局,您能否为我指示一个从自定义布局呈现的首选项示例? - ab11
4个回答

18

一种解决方法是扩展DialogPreference,允许为首选项对话框设置自定义布局。这样,您就可以列出一个偏好设置,并在点击它时获得带有自定义设置UI的对话框。

 <com.xyz.MyPreference 
           android:dialogLayout="@layout/yourlayout"
           android:dialogTitle="Dialog Title"
            android:dialogMessage="Dialog summary"
            android:key="preference_key"
            android:title="Preference Title"
            android:summary="Preference summary"
            android:defaultValue="Default Value" /> 

还有这个类

class MyPreference extends DialogPreference {
// along with constructors, you will want to override
    @Override
    protected void onBindDialogView(View view) {
        super.onBindDialogView(view);
        // view is your layout expanded and added to the dialog
            // find and hang on to your views here, add click listeners etc
            // basically things you would do in onCreate
        mTextView = (TextView)view.findViewById(R.Id.mytextview);
        }

        @Override
        protected void onDialogClosed(boolean positiveResult) {
           super.onDialogClosed(positiveResult);

            if (positiveResult) {
                // deal with persisting your values here
            }
        }
}

显然还有一些其他的细节,但这就是基本思路。


我考虑过这个问题,但真的不想扩展DialogPreference。我不想要“确定”和“取消”按钮,也不想要对话框样式。我希望自定义Preference具有与启动它的PreferenceActivity类似的样式。 - ab11
你可以使用Preference并设置android:layout为你的xml布局来实现相同的效果。这样,你的布局将显示在Preference屏幕中。你也可以只覆盖widget部分并使用android:widgetLayout。然后,在onBindView上监听以将监听器附加到你的控件。 - slund
不想让事情变得困难,但是如果我在主要的PreferenceActivity中添加一个PreferenceScreen,并设置该PreferenceScreen的布局,则自定义布局会显示在主PreferenceActivity中,而PreferenceScreen的标题和摘要则不会显示。我希望标题和摘要能够显示,并且在点击首选项时,在新窗口中显示自定义布局。 - ab11
你并不是在刁难,你只是想要自己想要的东西;-) 你可以创建一个Preference扩展,并覆盖onClick以触发意图到另一个PreferenceActivity。 - slund
类似于alibi给出的答案? - ab11
创建新的首选项扩展并覆盖onClick非常好用。谢谢。 - ab11

7

alibi的解决方案-在<PreferenceScreen>条目中定义意图-经过了很多targetPackagetargetClass字段的试错后,对我有效。

targetPackage需要是我的应用程序的包名称的完整路径(即AndroidManifest.xml文件中的package=条目)。 targetClass需要是Activity的完整路径-包括包名称,即使Activity与Application在同一个包中也是如此。

当然,应用程序的AndroidManifest.xml文件还需要一个Activity的条目。我没有为此条目定义<intent-filter>,可能是因为actionMAIN(无论Activity是否与Application在同一个包中都是如此)。

例如:应用程序的包为com.thissocialworld。 我想从PreferencesScreen启动的Activity位于名为com.coolcommon的包中,Activity类为com.thissocialworld.SpecialPreferences。 在<PreferenceScreen>中的条目如下所示:

<intent android:action="android.intent.action.MAIN"
 android:targetPackage="com.thissocialworld"
 android:targetClass="com.thissocialworld.SpecialPreferences"/>

如果需要访问 PreferencesManager,我可能会尝试将 action.MAIN 更改为 action.PREFERENCES

(附言:这是我在此的第一篇帖子,我无法弄清楚如何将其发布为对 alibi 开始的讨论的评论。)


5
您可能希望实现与我类似的功能,从相册或相机上传照片。请参考以下内容:
package com.atClass.lmt;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.provider.MediaStore;
import android.util.Log;


public class Prefs extends PreferenceActivity{
    //public static final int FLAG_ACTIVITY_CLEAR_TOP = 1;
    private static final int MEDIA_IMAGE_REQUEST_CODE = 1;
    private static final int CAMERA_IMAGE_REQUEST_CODE = 2;
    public static Uri cImageUri;

    public static Context cContext;
    public static Activity cActivity;

    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.settings);

        this.cContext = (Context)this;
        this.cActivity = (Activity)this;

        Preference customPref = (Preference) findPreference("user_display_picture");
        customPref.setOnPreferenceClickListener(
                new OnPreferenceClickListener() {
                    public boolean onPreferenceClick(Preference preference) {
                        return imageUploadDialog();
                    }
                });
    }

    protected void onStop(){
        super.onStop();
        MapTools.createMapView(false);
        Lmt.serviceBinder.serviceThread("loginDevice");
    }

    public boolean imageUploadDialog(){
        final CharSequence[] items = {"Take picture now","Upload from gallery"};
        AlertDialog.Builder lAlertDialog = new AlertDialog.Builder(cContext);
        lAlertDialog.setTitle("Upload action");
        lAlertDialog.setCancelable(true);
        lAlertDialog.setItems(items,
                new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialogInterface, int i){
                //Toast.makeText(getApplicationContext(), "Selected item: " +i,  Toast.LENGTH_SHORT).show();
                if (i == 0){
                    attachCameraImage();
                }
                if (i == 1){
                    attachGalleryImage();
                }
            }
        });
        lAlertDialog.setIcon(R.drawable.click_to_url);
        lAlertDialog.show();
        return true;
    }

    public void attachGalleryImage(){
        Intent getImageFromGalleryIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.INTERNAL_CONTENT_URI);
        startActivityForResult(getImageFromGalleryIntent, MEDIA_IMAGE_REQUEST_CODE);
    }

    public void attachCameraImage(){
        String fileName = "testphoto.jpg";
        ContentValues values = new ContentValues();
        values.put(MediaStore.Images.Media.TITLE, fileName);
        values.put(MediaStore.Images.Media.DESCRIPTION,"Image capture by camera");
        values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
        cImageUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, cImageUri);
        intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
        startActivityForResult(intent, CAMERA_IMAGE_REQUEST_CODE);
    }

    protected final void onActivityResult(final int requestCode, final int resultCode, final Intent i) {
        Log.d(Global.TAG,"--> Received callback with:" + resultCode);
        super.onActivityResult(requestCode, resultCode, i);
        if(resultCode == RESULT_OK) {
            Log.d(Global.TAG,"--> Result OK with:" + requestCode);
            switch(requestCode) {
            case MEDIA_IMAGE_REQUEST_CODE:
                Log.d(Global.TAG,"--> MEDIA_IMAGE_REQUEST_CODE");
                Gui.GuiProgressDialog.showLoadingSpinner(cActivity);
                cImageUri = i.getData();
                if (cImageUri == null){Log.d(Global.TAG,"--> ImageURI is null!");}
                Lmt.serviceBinder.serviceThread("uploadMemberPicture");
                break;
            case CAMERA_IMAGE_REQUEST_CODE:
                Log.d(Global.TAG,"--> CAMERA_IMAGE_REQUEST_CODE");
                //cImageUri = i.getData();
                if (cImageUri == null){Log.d(Global.TAG,"--> ImageURI is null!");}
                Lmt.serviceBinder.serviceThread("uploadMemberPicture");
                break;
            }
        }
    }
}

0
你可以使用自定义活动来实现这个功能。只需设计该活动并将其作为首选项屏幕包含在您的首选项中即可。
<PreferenceScreen  android:summary="@string/pref_summary" android:title="@string/title_summary">
  <intent android:action="android.intent.action.MAIN"
     android:targetPackage="targetPackage"
     android:targetClass="targetClass"/>
</PreferenceScreen>

不要忘记在清单文件中注册您的活动!

谢谢,这很有道理。但是,尽管我绝对确定在清单中注册了该活动,但它仍然出现以下异常。你有什么想法吗?“android.content.ActivityNotFoundException:无法找到显式活动类{com.me.CustomPreference};您是否在AndroidManifest.xml中声明了此活动?” - ab11
不是真的。我无法让这个建议起作用,在xml中定义新意图。如果我扩展Prefence并覆盖onClick()以启动新意图,它可以正常工作。 - ab11

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接