PreferenceFragment.java revision 301061d5655864f03f9bab0a28f7c3f2e28a0a04
1/* 2 * Copyright (C) 2015 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 17package android.support.v14.preference; 18 19import android.app.DialogFragment; 20import android.app.Fragment; 21import android.content.Context; 22import android.content.res.TypedArray; 23import android.graphics.Canvas; 24import android.graphics.Rect; 25import android.graphics.drawable.Drawable; 26import android.os.Bundle; 27import android.os.Handler; 28import android.os.Message; 29import android.support.annotation.Nullable; 30import android.support.annotation.XmlRes; 31import android.support.v4.view.ViewCompat; 32import android.support.v7.preference.DialogPreference; 33import android.support.v7.preference.EditTextPreference; 34import android.support.v7.preference.ListPreference; 35import android.support.v7.preference.Preference; 36import android.support.v7.preference.PreferenceGroupAdapter; 37import android.support.v7.preference.PreferenceManager; 38import android.support.v7.preference.PreferenceScreen; 39import android.support.v7.preference.PreferenceViewHolder; 40import android.support.v7.widget.LinearLayoutManager; 41import android.support.v7.widget.RecyclerView; 42import android.util.TypedValue; 43import android.view.ContextThemeWrapper; 44import android.view.LayoutInflater; 45import android.view.View; 46import android.view.ViewGroup; 47 48/** 49 * Shows a hierarchy of {@link Preference} objects as 50 * lists. These preferences will 51 * automatically save to {@link android.content.SharedPreferences} as the user interacts with 52 * them. To retrieve an instance of {@link android.content.SharedPreferences} that the 53 * preference hierarchy in this fragment will use, call 54 * {@link PreferenceManager#getDefaultSharedPreferences(android.content.Context)} 55 * with a context in the same package as this fragment. 56 * <p> 57 * Furthermore, the preferences shown will follow the visual style of system 58 * preferences. It is easy to create a hierarchy of preferences (that can be 59 * shown on multiple screens) via XML. For these reasons, it is recommended to 60 * use this fragment (as a superclass) to deal with preferences in applications. 61 * <p> 62 * A {@link PreferenceScreen} object should be at the top of the preference 63 * hierarchy. Furthermore, subsequent {@link PreferenceScreen} in the hierarchy 64 * denote a screen break--that is the preferences contained within subsequent 65 * {@link PreferenceScreen} should be shown on another screen. The preference 66 * framework handles this by calling {@link #onNavigateToScreen(PreferenceScreen)}. 67 * <p> 68 * The preference hierarchy can be formed in multiple ways: 69 * <li> From an XML file specifying the hierarchy 70 * <li> From different {@link android.app.Activity Activities} that each specify its own 71 * preferences in an XML file via {@link android.app.Activity} meta-data 72 * <li> From an object hierarchy rooted with {@link PreferenceScreen} 73 * <p> 74 * To inflate from XML, use the {@link #addPreferencesFromResource(int)}. The 75 * root element should be a {@link PreferenceScreen}. Subsequent elements can point 76 * to actual {@link Preference} subclasses. As mentioned above, subsequent 77 * {@link PreferenceScreen} in the hierarchy will result in the screen break. 78 * <p> 79 * To specify an object hierarchy rooted with {@link PreferenceScreen}, use 80 * {@link #setPreferenceScreen(PreferenceScreen)}. 81 * <p> 82 * As a convenience, this fragment implements a click listener for any 83 * preference in the current hierarchy, see 84 * {@link #onPreferenceTreeClick(Preference)}. 85 * 86 * <div class="special reference"> 87 * <h3>Developer Guides</h3> 88 * <p>For information about using {@code PreferenceFragment}, 89 * read the <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a> 90 * guide.</p> 91 * </div> 92 * 93 * <a name="SampleCode"></a> 94 * <h3>Sample Code</h3> 95 * 96 * <p>The following sample code shows a simple preference fragment that is 97 * populated from a resource. The resource it loads is:</p> 98 * 99 * {@sample development/samples/ApiDemos/res/xml/preferences.xml preferences} 100 * 101 * <p>The fragment implementation itself simply populates the preferences 102 * when created. Note that the preferences framework takes care of loading 103 * the current values out of the app preferences and writing them when changed:</p> 104 * 105 * {@sample development/samples/ApiDemos/src/com/example/android/apis/preference/FragmentPreferences.java 106 * fragment} 107 * 108 * @see Preference 109 * @see PreferenceScreen 110 */ 111public abstract class PreferenceFragment extends Fragment implements 112 PreferenceManager.OnPreferenceTreeClickListener, 113 PreferenceManager.OnDisplayPreferenceDialogListener, 114 PreferenceManager.OnNavigateToScreenListener, 115 DialogPreference.TargetFragment { 116 117 /** 118 * Fragment argument used to specify the tag of the desired root 119 * {@link android.support.v7.preference.PreferenceScreen} object. 120 */ 121 public static final String ARG_PREFERENCE_ROOT = 122 "android.support.v7.preference.PreferenceFragmentCompat.PREFERENCE_ROOT"; 123 124 private static final String PREFERENCES_TAG = "android:preferences"; 125 126 private static final String DIALOG_FRAGMENT_TAG = 127 "android.support.v14.preference.PreferenceFragment.DIALOG"; 128 129 private PreferenceManager mPreferenceManager; 130 private RecyclerView mList; 131 private boolean mHavePrefs; 132 private boolean mInitDone; 133 134 private Context mStyledContext; 135 136 private int mLayoutResId = R.layout.preference_list_fragment; 137 138 private final DividerDecoration mDividerDecoration = new DividerDecoration(); 139 140 private static final int MSG_BIND_PREFERENCES = 1; 141 private Handler mHandler = new Handler() { 142 @Override 143 public void handleMessage(Message msg) { 144 switch (msg.what) { 145 146 case MSG_BIND_PREFERENCES: 147 bindPreferences(); 148 break; 149 } 150 } 151 }; 152 153 final private Runnable mRequestFocus = new Runnable() { 154 public void run() { 155 mList.focusableViewAvailable(mList); 156 } 157 }; 158 159 /** 160 * Interface that PreferenceFragment's containing activity should 161 * implement to be able to process preference items that wish to 162 * switch to a specified fragment. 163 */ 164 public interface OnPreferenceStartFragmentCallback { 165 /** 166 * Called when the user has clicked on a Preference that has 167 * a fragment class name associated with it. The implementation 168 * should instantiate and switch to an instance of the given 169 * fragment. 170 * @param caller The fragment requesting navigation. 171 * @param pref The preference requesting the fragment. 172 * @return true if the fragment creation has been handled 173 */ 174 boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref); 175 } 176 177 /** 178 * Interface that PreferenceFragment's containing activity should 179 * implement to be able to process preference items that wish to 180 * switch to a new screen of preferences. 181 */ 182 public interface OnPreferenceStartScreenCallback { 183 /** 184 * Called when the user has clicked on a PreferenceScreen item in order to navigate to a new 185 * screen of preferences. 186 * @param caller The fragment requesting navigation. 187 * @param pref The preference screen to navigate to. 188 * @return true if the screen navigation has been handled 189 */ 190 boolean onPreferenceStartScreen(PreferenceFragment caller, PreferenceScreen pref); 191 } 192 193 public interface OnPreferenceDisplayDialogCallback { 194 195 /** 196 * 197 * @param caller The fragment containing the preference requesting the dialog. 198 * @param pref The preference requesting the dialog. 199 * @return true if the dialog creation has been handled. 200 */ 201 boolean onPreferenceDisplayDialog(PreferenceFragment caller, Preference pref); 202 } 203 204 @Override 205 public void onCreate(Bundle savedInstanceState) { 206 super.onCreate(savedInstanceState); 207 final TypedValue tv = new TypedValue(); 208 getActivity().getTheme().resolveAttribute(R.attr.preferenceTheme, tv, true); 209 final int theme = tv.resourceId; 210 if (theme <= 0) { 211 throw new IllegalStateException("Must specify preferenceTheme in theme"); 212 } 213 mStyledContext = new ContextThemeWrapper(getActivity(), theme); 214 215 mPreferenceManager = new PreferenceManager(mStyledContext); 216 mPreferenceManager.setOnNavigateToScreenListener(this); 217 final Bundle args = getArguments(); 218 final String rootKey; 219 if (args != null) { 220 rootKey = getArguments().getString(ARG_PREFERENCE_ROOT); 221 } else { 222 rootKey = null; 223 } 224 onCreatePreferences(savedInstanceState, rootKey); 225 } 226 227 /** 228 * Called during {@link #onCreate(Bundle)} to supply the preferences for this fragment. 229 * Subclasses are expected to call {@link #setPreferenceScreen(PreferenceScreen)} either 230 * directly or via helper methods such as {@link #addPreferencesFromResource(int)}. 231 * 232 * @param savedInstanceState If the fragment is being re-created from 233 * a previous saved state, this is the state. 234 * @param rootKey If non-null, this preference fragment should be rooted at the 235 * {@link android.support.v7.preference.PreferenceScreen} with this key. 236 */ 237 public abstract void onCreatePreferences(Bundle savedInstanceState, String rootKey); 238 239 @Override 240 public View onCreateView(LayoutInflater inflater, ViewGroup container, 241 Bundle savedInstanceState) { 242 243 TypedArray a = mStyledContext.obtainStyledAttributes(null, 244 R.styleable.PreferenceFragment, 245 R.attr.preferenceFragmentStyle, 246 0); 247 248 mLayoutResId = a.getResourceId(R.styleable.PreferenceFragment_android_layout, mLayoutResId); 249 250 final Drawable divider = a.getDrawable(R.styleable.PreferenceFragment_android_divider); 251 final int dividerHeight = a.getDimensionPixelSize( 252 R.styleable.PreferenceFragment_android_dividerHeight, -1); 253 254 a.recycle(); 255 256 // Need to theme the inflater to pick up the preferenceFragmentListStyle 257 final TypedValue tv = new TypedValue(); 258 getActivity().getTheme().resolveAttribute(R.attr.preferenceTheme, tv, true); 259 final int theme = tv.resourceId; 260 261 final Context themedContext = new ContextThemeWrapper(inflater.getContext(), theme); 262 final LayoutInflater themedInflater = inflater.cloneInContext(themedContext); 263 264 final View view = themedInflater.inflate(mLayoutResId, container, false); 265 266 final View rawListContainer = view.findViewById(R.id.list_container); 267 if (!(rawListContainer instanceof ViewGroup)) { 268 throw new RuntimeException("Content has view with id attribute 'R.id.list_container' " 269 + "that is not a ViewGroup class"); 270 } 271 272 final ViewGroup listContainer = (ViewGroup) rawListContainer; 273 274 final RecyclerView listView = onCreateRecyclerView(themedInflater, listContainer, 275 savedInstanceState); 276 if (listView == null) { 277 throw new RuntimeException("Could not create RecyclerView"); 278 } 279 280 mList = listView; 281 282 listView.addItemDecoration(mDividerDecoration); 283 setDivider(divider); 284 if (dividerHeight != -1) { 285 setDividerHeight(dividerHeight); 286 } 287 288 listContainer.addView(mList); 289 mHandler.post(mRequestFocus); 290 return view; 291 } 292 293 /** 294 * Sets the drawable that will be drawn between each item in the list. 295 * <p> 296 * <strong>Note:</strong> If the drawable does not have an intrinsic 297 * height, you should also call {@link #setDividerHeight(int)}. 298 * 299 * @param divider the drawable to use 300 * @attr ref R.styleable#PreferenceFragment_android_divider 301 */ 302 public void setDivider(Drawable divider) { 303 mDividerDecoration.setDivider(divider); 304 } 305 306 /** 307 * Sets the height of the divider that will be drawn between each item in the list. Calling 308 * this will override the intrinsic height as set by {@link #setDivider(Drawable)} 309 * 310 * @param height The new height of the divider in pixels. 311 * @attr ref R.styleable#PreferenceFragment_android_dividerHeight 312 */ 313 public void setDividerHeight(int height) { 314 mDividerDecoration.setDividerHeight(height); 315 } 316 317 @Override 318 public void onActivityCreated(Bundle savedInstanceState) { 319 super.onActivityCreated(savedInstanceState); 320 321 if (mHavePrefs) { 322 bindPreferences(); 323 } 324 325 mInitDone = true; 326 327 if (savedInstanceState != null) { 328 Bundle container = savedInstanceState.getBundle(PREFERENCES_TAG); 329 if (container != null) { 330 final PreferenceScreen preferenceScreen = getPreferenceScreen(); 331 if (preferenceScreen != null) { 332 preferenceScreen.restoreHierarchyState(container); 333 } 334 } 335 } 336 } 337 338 @Override 339 public void onStart() { 340 super.onStart(); 341 mPreferenceManager.setOnPreferenceTreeClickListener(this); 342 mPreferenceManager.setOnDisplayPreferenceDialogListener(this); 343 } 344 345 @Override 346 public void onStop() { 347 super.onStop(); 348 mPreferenceManager.setOnPreferenceTreeClickListener(null); 349 mPreferenceManager.setOnDisplayPreferenceDialogListener(null); 350 } 351 352 @Override 353 public void onDestroyView() { 354 mList = null; 355 mHandler.removeCallbacks(mRequestFocus); 356 mHandler.removeMessages(MSG_BIND_PREFERENCES); 357 super.onDestroyView(); 358 } 359 360 @Override 361 public void onSaveInstanceState(Bundle outState) { 362 super.onSaveInstanceState(outState); 363 364 final PreferenceScreen preferenceScreen = getPreferenceScreen(); 365 if (preferenceScreen != null) { 366 Bundle container = new Bundle(); 367 preferenceScreen.saveHierarchyState(container); 368 outState.putBundle(PREFERENCES_TAG, container); 369 } 370 } 371 372 /** 373 * Returns the {@link PreferenceManager} used by this fragment. 374 * @return The {@link PreferenceManager}. 375 */ 376 public PreferenceManager getPreferenceManager() { 377 return mPreferenceManager; 378 } 379 380 /** 381 * Sets the root of the preference hierarchy that this fragment is showing. 382 * 383 * @param preferenceScreen The root {@link PreferenceScreen} of the preference hierarchy. 384 */ 385 public void setPreferenceScreen(PreferenceScreen preferenceScreen) { 386 if (mPreferenceManager.setPreferences(preferenceScreen) && preferenceScreen != null) { 387 onUnbindPreferences(); 388 mHavePrefs = true; 389 if (mInitDone) { 390 postBindPreferences(); 391 } 392 } 393 } 394 395 /** 396 * Gets the root of the preference hierarchy that this fragment is showing. 397 * 398 * @return The {@link PreferenceScreen} that is the root of the preference 399 * hierarchy. 400 */ 401 public PreferenceScreen getPreferenceScreen() { 402 return mPreferenceManager.getPreferenceScreen(); 403 } 404 405 /** 406 * Inflates the given XML resource and adds the preference hierarchy to the current 407 * preference hierarchy. 408 * 409 * @param preferencesResId The XML resource ID to inflate. 410 */ 411 public void addPreferencesFromResource(@XmlRes int preferencesResId) { 412 requirePreferenceManager(); 413 414 setPreferenceScreen(mPreferenceManager.inflateFromResource(mStyledContext, 415 preferencesResId, getPreferenceScreen())); 416 } 417 418 /** 419 * Inflates the given XML resource and replaces the current preference hierarchy (if any) with 420 * the preference hierarchy rooted at {@code key}. 421 * 422 * @param preferencesResId The XML resource ID to inflate. 423 * @param key The preference key of the {@link android.support.v7.preference.PreferenceScreen} 424 * to use as the root of the preference hierarchy, or null to use the root 425 * {@link android.support.v7.preference.PreferenceScreen}. 426 */ 427 public void setPreferencesFromResource(@XmlRes int preferencesResId, @Nullable String key) { 428 requirePreferenceManager(); 429 430 final PreferenceScreen xmlRoot = mPreferenceManager.inflateFromResource(mStyledContext, 431 preferencesResId, null); 432 433 final Preference root; 434 if (key != null) { 435 root = xmlRoot.findPreference(key); 436 if (!(root instanceof PreferenceScreen)) { 437 throw new IllegalArgumentException("Preference object with key " + key 438 + " is not a PreferenceScreen"); 439 } 440 } else { 441 root = xmlRoot; 442 } 443 444 setPreferenceScreen((PreferenceScreen) root); 445 } 446 447 /** 448 * {@inheritDoc} 449 */ 450 public boolean onPreferenceTreeClick(Preference preference) { 451 if (preference.getFragment() != null) { 452 boolean handled = false; 453 if (getCallbackFragment() instanceof OnPreferenceStartFragmentCallback) { 454 handled = ((OnPreferenceStartFragmentCallback) getCallbackFragment()) 455 .onPreferenceStartFragment(this, preference); 456 } 457 if (!handled && getActivity() instanceof OnPreferenceStartFragmentCallback){ 458 handled = ((OnPreferenceStartFragmentCallback) getActivity()) 459 .onPreferenceStartFragment(this, preference); 460 } 461 return handled; 462 } 463 return false; 464 } 465 466 /** 467 * Called by 468 * {@link android.support.v7.preference.PreferenceScreen#onClick()} in order to navigate to a 469 * new screen of preferences. Calls 470 * {@link PreferenceFragment.OnPreferenceStartScreenCallback#onPreferenceStartScreen} 471 * if the target fragment or containing activity implements 472 * {@link PreferenceFragment.OnPreferenceStartScreenCallback}. 473 * @param preferenceScreen The {@link android.support.v7.preference.PreferenceScreen} to 474 * navigate to. 475 */ 476 @Override 477 public void onNavigateToScreen(PreferenceScreen preferenceScreen) { 478 boolean handled = false; 479 if (getCallbackFragment() instanceof OnPreferenceStartScreenCallback) { 480 handled = ((OnPreferenceStartScreenCallback) getCallbackFragment()) 481 .onPreferenceStartScreen(this, preferenceScreen); 482 } 483 if (!handled && getActivity() instanceof OnPreferenceStartScreenCallback) { 484 ((OnPreferenceStartScreenCallback) getActivity()) 485 .onPreferenceStartScreen(this, preferenceScreen); 486 } 487 } 488 489 /** 490 * Finds a {@link Preference} based on its key. 491 * 492 * @param key The key of the preference to retrieve. 493 * @return The {@link Preference} with the key, or null. 494 * @see android.support.v7.preference.PreferenceGroup#findPreference(CharSequence) 495 */ 496 public Preference findPreference(CharSequence key) { 497 if (mPreferenceManager == null) { 498 return null; 499 } 500 return mPreferenceManager.findPreference(key); 501 } 502 503 private void requirePreferenceManager() { 504 if (mPreferenceManager == null) { 505 throw new RuntimeException("This should be called after super.onCreate."); 506 } 507 } 508 509 private void postBindPreferences() { 510 if (mHandler.hasMessages(MSG_BIND_PREFERENCES)) return; 511 mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget(); 512 } 513 514 private void bindPreferences() { 515 final PreferenceScreen preferenceScreen = getPreferenceScreen(); 516 if (preferenceScreen != null) { 517 getListView().setAdapter(onCreateAdapter(preferenceScreen)); 518 preferenceScreen.onAttached(); 519 } 520 onBindPreferences(); 521 } 522 523 /** @hide */ 524 protected void onBindPreferences() { 525 } 526 527 /** @hide */ 528 protected void onUnbindPreferences() { 529 } 530 531 public final RecyclerView getListView() { 532 return mList; 533 } 534 535 /** 536 * Creates the {@link android.support.v7.widget.RecyclerView} used to display the preferences. 537 * Subclasses may override this to return a customized 538 * {@link android.support.v7.widget.RecyclerView}. 539 * @param inflater The LayoutInflater object that can be used to inflate the 540 * {@link android.support.v7.widget.RecyclerView}. 541 * @param parent The parent {@link android.view.View} that the RecyclerView will be attached to. 542 * This method should not add the view itself, but this can be used to generate 543 * the LayoutParams of the view. 544 * @param savedInstanceState If non-null, this view is being re-constructed from a previous 545 * saved state as given here 546 * @return A new RecyclerView object to be placed into the view hierarchy 547 */ 548 public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent, 549 Bundle savedInstanceState) { 550 RecyclerView recyclerView = (RecyclerView) inflater 551 .inflate(R.layout.preference_recyclerview, parent, false); 552 553 recyclerView.setLayoutManager(onCreateLayoutManager()); 554 555 return recyclerView; 556 } 557 558 /** 559 * Called from {@link #onCreateRecyclerView} to create the 560 * {@link android.support.v7.widget.RecyclerView.LayoutManager} for the created 561 * {@link android.support.v7.widget.RecyclerView}. 562 * @return A new {@link android.support.v7.widget.RecyclerView.LayoutManager} instance. 563 */ 564 public RecyclerView.LayoutManager onCreateLayoutManager() { 565 return new LinearLayoutManager(getActivity()); 566 } 567 568 /** 569 * Creates the root adapter. 570 * 571 * @param preferenceScreen Preference screen object to create the adapter for. 572 * @return An adapter that contains the preferences contained in this {@link PreferenceScreen}. 573 */ 574 protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) { 575 return new PreferenceGroupAdapter(preferenceScreen); 576 } 577 578 /** 579 * Called when a preference in the tree requests to display a dialog. Subclasses should 580 * override this method to display custom dialogs or to handle dialogs for custom preference 581 * classes. 582 * 583 * @param preference The Preference object requesting the dialog. 584 */ 585 @Override 586 public void onDisplayPreferenceDialog(Preference preference) { 587 588 boolean handled = false; 589 if (getCallbackFragment() instanceof OnPreferenceDisplayDialogCallback) { 590 handled = ((OnPreferenceDisplayDialogCallback) getCallbackFragment()) 591 .onPreferenceDisplayDialog(this, preference); 592 } 593 if (!handled && getActivity() instanceof OnPreferenceDisplayDialogCallback) { 594 handled = ((OnPreferenceDisplayDialogCallback) getActivity()) 595 .onPreferenceDisplayDialog(this, preference); 596 } 597 598 if (handled) { 599 return; 600 } 601 602 // check if dialog is already showing 603 if (getFragmentManager().findFragmentByTag(DIALOG_FRAGMENT_TAG) != null) { 604 return; 605 } 606 607 final DialogFragment f; 608 if (preference instanceof EditTextPreference) { 609 f = EditTextPreferenceDialogFragment.newInstance(preference.getKey()); 610 } else if (preference instanceof ListPreference) { 611 f = ListPreferenceDialogFragment.newInstance(preference.getKey()); 612 } else if (preference instanceof MultiSelectListPreference) { 613 f = MultiSelectListPreferenceDialogFragment.newInstance(preference.getKey()); 614 } else { 615 throw new IllegalArgumentException("Tried to display dialog for unknown " + 616 "preference type. Did you forget to override onDisplayPreferenceDialog()?"); 617 } 618 f.setTargetFragment(this, 0); 619 f.show(getFragmentManager(), DIALOG_FRAGMENT_TAG); 620 } 621 622 /** 623 * Basically a wrapper for getParentFragment which is v17+. Used by the leanback preference lib. 624 * @return Fragment to possibly use as a callback 625 * @hide 626 */ 627 public Fragment getCallbackFragment() { 628 return null; 629 } 630 631 private class DividerDecoration extends RecyclerView.ItemDecoration { 632 633 private Drawable mDivider; 634 private int mDividerHeight; 635 636 @Override 637 public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { 638 if (mDivider == null) { 639 return; 640 } 641 final int childCount = parent.getChildCount(); 642 final int width = parent.getWidth(); 643 for (int childViewIndex = 0; childViewIndex < childCount; childViewIndex++) { 644 final View view = parent.getChildAt(childViewIndex); 645 if (shouldDrawDividerAbove(view, parent)) { 646 int top = (int) ViewCompat.getY(view); 647 mDivider.setBounds(0, top, width, top + mDividerHeight); 648 mDivider.draw(c); 649 } 650 if (shouldDrawDividerBelow(view, parent)) { 651 int top = (int) ViewCompat.getY(view) + view.getHeight(); 652 mDivider.setBounds(0, top, width, top + mDividerHeight); 653 mDivider.draw(c); 654 } 655 } 656 } 657 658 @Override 659 public void getItemOffsets(Rect outRect, View view, RecyclerView parent, 660 RecyclerView.State state) { 661 if (shouldDrawDividerAbove(view, parent)) { 662 outRect.top = mDividerHeight; 663 } 664 if (shouldDrawDividerBelow(view, parent)) { 665 outRect.bottom = mDividerHeight; 666 } 667 } 668 669 private boolean shouldDrawDividerAbove(View view, RecyclerView parent) { 670 final RecyclerView.ViewHolder holder = parent.getChildViewHolder(view); 671 return holder.getAdapterPosition() == 0 && 672 ((PreferenceViewHolder) holder).isDividerAllowedAbove(); 673 } 674 675 private boolean shouldDrawDividerBelow(View view, RecyclerView parent) { 676 final PreferenceViewHolder holder = 677 (PreferenceViewHolder) parent.getChildViewHolder(view); 678 boolean nextAllowed = true; 679 int index = parent.indexOfChild(view); 680 if (index < parent.getChildCount() - 1) { 681 final View nextView = parent.getChildAt(index + 1); 682 final PreferenceViewHolder nextHolder = 683 (PreferenceViewHolder) parent.getChildViewHolder(nextView); 684 nextAllowed = nextHolder.isDividerAllowedAbove(); 685 } 686 return nextAllowed && holder.isDividerAllowedBelow(); 687 } 688 689 public void setDivider(Drawable divider) { 690 if (divider != null) { 691 mDividerHeight = divider.getIntrinsicHeight(); 692 } else { 693 mDividerHeight = 0; 694 } 695 mDivider = divider; 696 mList.invalidateItemDecorations(); 697 } 698 699 public void setDividerHeight(int dividerHeight) { 700 mDividerHeight = dividerHeight; 701 mList.invalidateItemDecorations(); 702 } 703 } 704} 705