2013-06-02 1 views
3

У меня есть собственный класс, расширяющий DialogPreference. Он отлично работает при запуске из меню «Предпочтения». Я хочу также запустить его из Activity. Ниже приведен мой класс DialogPreference, в котором я выставил метод showDialog() suggested by this thread. Когда я это называю, я получаю исключение Null Pointer Exception, но не смог понять, почему.Невозможно запустить DialogPreference программно (исключение Null Pointer Exception)

Ошибка вызывается в строке 27, которая находится в onBindDialogView(), где вызывается hText.setText().

package com.jumptuck.recipebrowser; 

import android.content.Context; 
import android.content.SharedPreferences; 
import android.preference.DialogPreference; 
import android.util.AttributeSet; 
import android.util.Log; 
import android.view.View; 
import android.widget.EditText; 

// Pop-up dialog used to set and modify host and login credentials 
public class HostCredentialsDialogPreference extends DialogPreference { 
    static final String TAG = "HostCredentialsDialogPreference"; 
    EditText hText, uText, pText; 

    public HostCredentialsDialogPreference(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     setDialogLayoutResource(R.layout.dialog_host_credentials); 
    } 

    @Override 
    protected void onBindDialogView(View view) { 
     super.onBindDialogView(view); 
     Log.d(TAG,"onBindDialogView"); 

     SharedPreferences sp = getSharedPreferences(); 
     hText.setText(sp.getString("host", "")); 
     uText.setText(sp.getString("username", "")); 
     pText.setText(sp.getString("password", "")); 
    } 

    @Override 
    protected View onCreateDialogView() { 
     // Guide for this technique found at: 
     // http://alexfu.tumblr.com/post/23683149440/android-dev-custom-dialogpreference 
     Log.d(TAG,"onCreateDialogView"); 
     View root = super.onCreateDialogView(); 
     hText = (EditText) root.findViewById(R.id.host); 
     uText = (EditText) root.findViewById(R.id.username); 
     pText = (EditText) root.findViewById(R.id.password); 
     return root; 
    } 

    @Override 
    protected void onDialogClosed(boolean positiveResult) { 
     super.onDialogClosed(positiveResult); 
     if (positiveResult){ 
      Log.d(TAG,"Clicked Save"); 
      SharedPreferences sp = getSharedPreferences(); 
      SharedPreferences.Editor editor = sp.edit(); 
      editor.putString("host", hText.getText().toString()); 
      editor.putString("username", uText.getText().toString()); 
      editor.putString("password", pText.getText().toString()); 
      editor.commit(); 
     } 
     else { 
      Log.d(TAG,"Clicked Cancel"); 
     } 
    } 

    void show() { 
     showDialog(null); 
    } 

} 

Я использую кнопку "Проверка" попытаться запустить диалог из другой деятельности:

public boolean onOptionsItemSelected(MenuItem item) { 
    Log.d(TAG, "onOptionsItemSelected"); 

    switch (item.getItemId()) { 
    case R.id.item_prefs: 
     startActivity(new Intent(this, PrefsActivity.class)); 
     return true; 
    case R.id.refresh: 
     if (credentialsExist()) { 
      refreshListView(); 
     } 
     return true; 
    case R.id.recipe_dir: 
     startActivity(new Intent(this, RecipeDisplayActivity.class)); 
     return true; 
    case R.id.testing: 
     HostCredentialsDialogPreference hc = new HostCredentialsDialogPreference(this, null); 
     hc.show(); 
     return true; 

    default: 
     return false; 
    } 
} 

Вот файлы XML для моих предпочтений и Dialog Предпочтение:

<?xml version="1.0" encoding="utf-8"?> 

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" > 
    <com.jumptuck.recipebrowser.HostCredentialsDialogPreference 
     android:key="dialog_credentials" 
     android:title="Server Address and Login" 
     android:summary="Set Host, Username and Password" /> 

</PreferenceScreen> 

и

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

    <TextView 
     android:id="@+id/dialog_hostname_label" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:text="@string/dialog_hint_uri" /> 

    <EditText 
     android:id="@+id/host" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:inputType="textUri" /> 

    <TextView 
     android:id="@+id/dialog_username_label" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:text="@string/dialog_hint_user" /> 

    <EditText 
     android:id="@+id/username" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:inputType="text" /> 

    <TextView 
     android:id="@+id/dialog_password_label" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:text="@string/dialog_hint_password" /> 

    <EditText 
     android:id="@+id/password" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:fontFamily="sans-serif" 
     android:inputType="textPassword" /> 

</LinearLayout> 

И, наконец, Logcat:

D/RecipeListActivity(5894): onOptionsItemSelected 
D/HostCredentialsDialogPreference(5894): onCreateDialogView 
D/HostCredentialsDialogPreference(5894): onBindDialogView 
D/AndroidRuntime(5894): Shutting down VM 
W/dalvikvm(5894): threadid=1: thread exiting with uncaught exception (group=0x40a13300) 
E/AndroidRuntime(5894): FATAL EXCEPTION: main 
E/AndroidRuntime(5894): java.lang.NullPointerException 
E/AndroidRuntime(5894): at com.jumptuck.recipebrowser.HostCredentialsDialogPreference.onBindDialogView(HostCredentialsDialogPreference.java:27) 
E/AndroidRuntime(5894): at android.preference.DialogPreference.showDialog(DialogPreference.java:289) 
E/AndroidRuntime(5894): at com.jumptuck.recipebrowser.HostCredentialsDialogPreference.show(HostCredentialsDialogPreference.java:62) 
E/AndroidRuntime(5894): at com.jumptuck.recipebrowser.RecipeListActivity.onOptionsItemSelected(RecipeListActivity.java:192) 
E/AndroidRuntime(5894): at android.app.Activity.onMenuItemSelected(Activity.java:2534) 
E/AndroidRuntime(5894): at com.android.internal.policy.impl.PhoneWindow.onMenuItemSelected(PhoneWindow.java:958) 
E/AndroidRuntime(5894): at com.android.internal.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:735) 
E/AndroidRuntime(5894): at com.android.internal.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:149) 
E/AndroidRuntime(5894): at com.android.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:874) 
E/AndroidRuntime(5894): at com.android.internal.view.menu.ListMenuPresenter.onItemClick(ListMenuPresenter.java:166) 
E/AndroidRuntime(5894): at android.widget.AdapterView.performItemClick(AdapterView.java:298) 
E/AndroidRuntime(5894): at android.widget.AbsListView.performItemClick(AbsListView.java:1086) 
E/AndroidRuntime(5894): at android.widget.AbsListView$PerformClick.run(AbsListView.java:2859) 
E/AndroidRuntime(5894): at android.widget.AbsListView$1.run(AbsListView.java:3533) 
E/AndroidRuntime(5894): at android.os.Handler.handleCallback(Handler.java:615) 
E/AndroidRuntime(5894): at android.os.Handler.dispatchMessage(Handler.java:92) 
E/AndroidRuntime(5894): at android.os.Looper.loop(Looper.java:137) 
E/AndroidRuntime(5894): at android.app.ActivityThread.main(ActivityThread.java:4745) 
E/AndroidRuntime(5894): at java.lang.reflect.Method.invokeNative(Native Method) 
E/AndroidRuntime(5894): at java.lang.reflect.Method.invoke(Method.java:511) 
E/AndroidRuntime(5894): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786) 
E/AndroidRuntime(5894): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553) 
E/AndroidRuntime(5894): at dalvik.system.NativeStart.main(Native Method) 
W/ActivityManager( 148): Force finishing activity com.jumptuck.recipebrowser/.RecipeListActivity 
W/WindowManager( 148): Failure taking screenshot for (246x410) to layer 21025 
W/ActivityManager( 148): Activity pause timeout for ActivityRecord{412097e0 com.jumptuck.recipebrowser/.RecipeListActivity} 
I/Choreographer( 281): Skipped 39 frames! The application may be doing too much work on its main thread. 
W/ActivityManager( 148): Activity destroy timeout for ActivityRecord{412097e0 com.jumptuck.recipebrowser/.RecipeListActivity} 

Кто-нибудь знает, что я делаю неправильно? Благодаря!

+0

Возможно потому, что ваш 'EditText' имеет не были инициализированы. Я рекомендую инициализировать их в вашем конструкторе. – ozbek

+0

Кроме того, попробуйте 'showDialog (getExtras());' вместо 'showDialog (null);' (я не уверен, что это компилируется). – ozbek

+0

@shoe rat: Я не думаю, что это проблема. Если я прокомментирую все строки setText в этом методе, я все равно получаю нулевой указатель: 'at android.preference.DialogPreference.showDialog (DialogPreference.java:297)' и функция getExtras() недоступна (не компиляции). –

ответ

2

Лучший ответ can be found here, но я думаю, что это требует лишь немного разъяснений, потому что этот ответ ошибочно предлагает две разные декларации стиля для манифеста.

Если вы хотите запустить одно диалоговое окно из Activity и по-прежнему сможете его запустить, выберите «Предпочтения», вам просто нужно создать Activity, который запустит Dialog. Затем эта активность может быть запущена как намерение в XML-предпочтении или другом мероприятии. Трюк приходит в том, как вы его стиль. Вы хотите, чтобы стиль выполнялся как диалог. Таким образом, диалог, который запускает Activity, будет выглядеть правильно. Побочным эффектом этого подхода является то, что плавающая панель действий будет отображаться в середине экрана за вашим диалоговым окном. Исправить это - использовать стиль диалога без ActionBar. Я использую Holo.Light тему, так что я положил это в моем AndroidManifest

<activity android:name=".DemoDialogActivity" 
      android:theme="@android:style/Theme.Holo.Light.Dialog.NoActionBar" /> 

Другая часть головоломки, чтобы убедиться, что вы называете отделку(); когда вы закончите (это последнее, что я сделал в OnClickListener для обеих кнопок). Если вы этого не сделаете, диалог закроется, но Activity все равно будет открыта, оставив небольшой пустой прямоугольник в середине затемненного экрана.

Вот рабочий пример деятельности:

import android.app.Activity; 
import android.app.AlertDialog; 
import android.content.DialogInterface; 
import android.os.Bundle; 
import android.view.LayoutInflater; 
import android.view.View; 

public class DemoDialogActivity extends Activity { 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     LayoutInflater lf = LayoutInflater.from(this); 
     // This adds XML elements as a custom view (optional): 
     final View customElementsView = lf.inflate(
       R.layout.activity_credentials, null); 
     AlertDialog alert = new AlertDialog.Builder(this) 
       // This adds the custom view to the Dialog (optional): 
       .setView(customElementsView) 
       .setTitle("This is the Title") 
       .setMessage("This is the AlertDialog message (optional)") 
       .setNegativeButton("Cancel", 
         new DialogInterface.OnClickListener() { 

          @Override 
          public void onClick(DialogInterface dialog, 
            int which) { 
           // Cancel was clicked; do something 
           // Close Activity 
           finish(); 
          } 
         }) 
       .setPositiveButton("OK", new DialogInterface.OnClickListener() { 

        @Override 
        public void onClick(DialogInterface dialog, int which) { 
         // OK was clicked; do something 
         // Close Activity 
         finish(); 
        } 
       }).create(); 

     // Show the dialog 
     alert.show(); 
    } 
} 

Launch программным способом:

Intent launch_dialog = new Intent(getApplicationContext(), 
    DemoDialogActivity.class); 
startActivity(launch_dialog); 

Или как предпочтения в XML:

<Preference 
    android:key="demo_dialog" 
    android:title="Title of item on Prefs screen" 
    android:summary="This will be small text below the title"> 
    <intent 
     android:action="android.intent.action.VIEW" 
     android:targetClass="com.example.package.DemoDialogActivity" 
     android:targetPackage="com.example.package" /> 
</Preference> 
0

Я работаю над этим в течение некоторого времени. Существует одно обходное решение, которое я считаю совершенно неэлегантным. Я могу просто использовать макет XML, чтобы построить DialogFragment путем внесения изменений в деятельности:

class HCDialog extends DialogFragment { 

    @Override 
    public Dialog onCreateDialog(Bundle savedInstanceState) { 
     // TODO Auto-generated method stub 
     AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); 
     LayoutInflater inflater = getActivity().getLayoutInflater(); 
     View view = inflater.inflate(R.layout.dialog_host_credentials, null); 
     builder.setView(view); 
     builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 

      @Override 
      public void onClick(DialogInterface dialog, int which) { 
       // TODO Auto-generated method stub 

      } 
     }) 
     .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { 

      @Override 
      public void onClick(DialogInterface dialog, int which) { 
       // TODO Auto-generated method stub 

      } 
     }); 
     builder.setTitle(R.string.dialog_title); 
     return builder.create(); 
    } 
} 

DialogFragment может быть запущен с помощью кнопки, как это (саз от кода вставленного как часть моего оригинального вопроса):

case R.id.testing: 
     FragmentManager fm = getFragmentManager(); 
     HCDialog hack_dialog = new HCDialog(); 
     hack_dialog.show(fm, null); 
     return true; 

Это работает. Но для меня это кажется довольно глупым, и я думаю, что я проделаю длинный путь. Тем более, что теперь я буду кодировать, чтобы обрабатывать постоянные значения предпочтений для двух версий того, что похоже на тот же самый диалог.

Есть ли лучший способ?