Added selection into forecast adapter.

This commit is contained in:
Dan Galpin
2015-05-25 03:09:28 -07:00
parent 8ccc514346
commit cf8b67e28b
12 changed files with 443 additions and 77 deletions

View File

@@ -18,6 +18,7 @@ package com.example.android.sunshine.app;
import android.content.Context;
import android.database.Cursor;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
@@ -46,6 +47,7 @@ public class ForecastAdapter extends RecyclerView.Adapter<ForecastAdapter.Foreca
final private Context mContext;
final private ForecastAdapterOnClickHandler mClickHandler;
final private View mEmptyView;
final private ItemChoiceManager mICM;
/**
* Cache of the children views for a forecast list item.
@@ -73,6 +75,7 @@ public class ForecastAdapter extends RecyclerView.Adapter<ForecastAdapter.Foreca
mCursor.moveToPosition(adapterPosition);
int dateColumnIndex = mCursor.getColumnIndex(WeatherContract.WeatherEntry.COLUMN_DATE);
mClickHandler.onClick(mCursor.getLong(dateColumnIndex), this);
mICM.onClick(this);
}
}
@@ -80,10 +83,12 @@ public class ForecastAdapter extends RecyclerView.Adapter<ForecastAdapter.Foreca
void onClick(Long date, ForecastAdapterViewHolder vh);
}
public ForecastAdapter(Context context, ForecastAdapterOnClickHandler dh, View emptyView) {
public ForecastAdapter(Context context, ForecastAdapterOnClickHandler dh, View emptyView, int choiceMode) {
mContext = context;
mClickHandler = dh;
mEmptyView = emptyView;
mICM = new ItemChoiceManager(this);
mICM.setChoiceMode(choiceMode);
}
/*
@@ -168,13 +173,25 @@ public class ForecastAdapter extends RecyclerView.Adapter<ForecastAdapter.Foreca
String lowString = Utility.formatTemperature(mContext, low);
forecastAdapterViewHolder.mLowTempView.setText(lowString);
forecastAdapterViewHolder.mLowTempView.setContentDescription(mContext.getString(R.string.a11y_low_temp, lowString));
mICM.onBindViewHolder(forecastAdapterViewHolder, position);
}
public void onRestoreInstanceState(Bundle savedInstanceState) {
mICM.onRestoreInstanceState(savedInstanceState);
}
public void onSaveInstanceState(Bundle outState) {
mICM.onSaveInstanceState(outState);
}
public void setUseTodayLayout(boolean useTodayLayout) {
mUseTodayLayout = useTodayLayout;
}
public int getSelectedItemPosition() {
return mICM.getSelectedItemPosition();
}
@Override
public int getItemViewType(int position) {
@@ -196,4 +213,11 @@ public class ForecastAdapter extends RecyclerView.Adapter<ForecastAdapter.Foreca
public Cursor getCursor() {
return mCursor;
}
public void selectView(RecyclerView.ViewHolder viewHolder) {
if ( viewHolder instanceof ForecastAdapterViewHolder ) {
ForecastAdapterViewHolder vfh = (ForecastAdapterViewHolder)viewHolder;
vfh.onClick(vfh.itemView);
}
}
}

View File

@@ -15,8 +15,10 @@
*/
package com.example.android.sunshine.app;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
@@ -27,6 +29,7 @@ import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -34,6 +37,8 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.AbsListView;
import android.widget.TextView;
import com.example.android.sunshine.app.data.WeatherContract;
@@ -48,7 +53,8 @@ public class ForecastFragment extends Fragment implements LoaderManager.LoaderCa
private RecyclerView mRecyclerView;
private int mPosition = RecyclerView.NO_POSITION;
private boolean mUseTodayLayout;
private boolean mUseTodayLayout, mAutoSelectView;
private int mChoiceMode;
private static final String SELECTED_KEY = "selected_position";
@@ -144,6 +150,16 @@ public class ForecastFragment extends Fragment implements LoaderManager.LoaderCa
return super.onOptionsItemSelected(item);
}
@Override
public void onInflate(Activity activity, AttributeSet attrs, Bundle savedInstanceState) {
super.onInflate(activity, attrs, savedInstanceState);
TypedArray a = activity.obtainStyledAttributes(attrs, R.styleable.ForecastFragment,
0, 0);
mChoiceMode = a.getInt(R.styleable.ForecastFragment_android_choiceMode, AbsListView.CHOICE_MODE_NONE);
mAutoSelectView = a.getBoolean(R.styleable.ForecastFragment_autoSelectView, false);
a.recycle();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
@@ -174,7 +190,7 @@ public class ForecastFragment extends Fragment implements LoaderManager.LoaderCa
);
mPosition = vh.getAdapterPosition();
}
}, emptyView);
}, emptyView, mChoiceMode);
// specify an adapter (see also next example)
mRecyclerView.setAdapter(mForecastAdapter);
@@ -184,11 +200,14 @@ public class ForecastFragment extends Fragment implements LoaderManager.LoaderCa
// does crazy lifecycle related things. It should feel like some stuff stretched out,
// or magically appeared to take advantage of room, but data or place in the app was never
// actually *lost*.
if (savedInstanceState != null && savedInstanceState.containsKey(SELECTED_KEY)) {
if (savedInstanceState != null) {
if (savedInstanceState.containsKey(SELECTED_KEY)) {
// The Recycler View probably hasn't even been populated yet. Actually perform the
// swapout in onLoadFinished.
mPosition = savedInstanceState.getInt(SELECTED_KEY);
}
mForecastAdapter.onRestoreInstanceState(savedInstanceState);
}
mForecastAdapter.setUseTodayLayout(mUseTodayLayout);
@@ -239,9 +258,11 @@ public class ForecastFragment extends Fragment implements LoaderManager.LoaderCa
if (mPosition != RecyclerView.NO_POSITION) {
outState.putInt(SELECTED_KEY, mPosition);
}
mForecastAdapter.onSaveInstanceState(outState);
super.onSaveInstanceState(outState);
}
@Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
// This is called when a new Loader needs to be created. This
@@ -274,6 +295,27 @@ public class ForecastFragment extends Fragment implements LoaderManager.LoaderCa
mRecyclerView.smoothScrollToPosition(mPosition);
}
updateEmptyView();
if ( data.getCount() > 0 ) {
mRecyclerView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
// Since we know we're going to get items, we keep the listener around until
// we see Children.
if (mRecyclerView.getChildCount() > 0) {
mRecyclerView.getViewTreeObserver().removeOnPreDrawListener(this);
int itemPosition = mForecastAdapter.getSelectedItemPosition();
if ( RecyclerView.NO_POSITION == itemPosition ) itemPosition = 0;
RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForAdapterPosition(itemPosition);
if ( null != vh && mAutoSelectView ) {
mForecastAdapter.selectView( vh );
}
return true;
}
return false;
}
});
}
}
@Override

View File

@@ -0,0 +1,237 @@
/*
* Copyright (C) 2015 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.example.android.sunshine.app;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v4.util.LongSparseArray;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.widget.AbsListView;
import android.widget.Checkable;
/**
* The ItemChoiceManager class keeps track of which positions have been selected. Note that it
* doesn't take advantage of new adapter features to track changes in the underlying data.
*/
public class ItemChoiceManager {
private final String LOG_TAG = MainActivity.class.getSimpleName();
private final String SELECTED_ITEMS_KEY = "SIK";
private int mChoiceMode;
private RecyclerView.Adapter mAdapter;
private RecyclerView.AdapterDataObserver mAdapterDataObserver = new RecyclerView.AdapterDataObserver() {
@Override
public void onChanged() {
super.onChanged();
if (mAdapter != null && mAdapter.hasStableIds())
confirmCheckedPositionsById(mAdapter.getItemCount());
}
};
private ItemChoiceManager() {
}
;
public ItemChoiceManager(RecyclerView.Adapter adapter) {
mAdapter = adapter;
}
/**
* How many positions in either direction we will search to try to
* find a checked item with a stable ID that moved position across
* a data set change. If the item isn't found it will be unselected.
*/
private static final int CHECK_POSITION_SEARCH_DISTANCE = 20;
/**
* Running state of which positions are currently checked
*/
SparseBooleanArray mCheckStates = new SparseBooleanArray();
/**
* Running state of which IDs are currently checked.
* If there is a value for a given key, the checked state for that ID is true
* and the value holds the last known position in the adapter for that id.
*/
LongSparseArray<Integer> mCheckedIdStates = new LongSparseArray<Integer>();
public void onClick(RecyclerView.ViewHolder vh) {
if (mChoiceMode == AbsListView.CHOICE_MODE_NONE)
return;
int checkedItemCount = mCheckStates.size();
int position = vh.getAdapterPosition();
if (position == RecyclerView.NO_POSITION) {
Log.d(LOG_TAG, "Unable to Set Item State");
return;
}
switch (mChoiceMode) {
case AbsListView.CHOICE_MODE_NONE:
break;
case AbsListView.CHOICE_MODE_SINGLE: {
boolean checked = mCheckStates.get(position, false);
if (!checked) {
for (int i = 0; i < checkedItemCount; i++) {
mAdapter.notifyItemChanged(mCheckStates.keyAt(i));
}
mCheckStates.clear();
mCheckStates.put(position, true);
mCheckedIdStates.clear();
mCheckedIdStates.put(mAdapter.getItemId(position), position);
}
// We directly call onBindViewHolder here because notifying that an item has
// changed on an item that has the focus causes it to lose focus, which makes
// keyboard navigation a bit annoying
mAdapter.onBindViewHolder(vh, position);
break;
}
case AbsListView.CHOICE_MODE_MULTIPLE: {
boolean checked = mCheckStates.get(position, false);
mCheckStates.put(position, !checked);
// We directly call onBindViewHolder here because notifying that an item has
// changed on an item that has the focus causes it to lose focus, which makes
// keyboard navigation a bit annoying
mAdapter.onBindViewHolder(vh, position);
break;
}
case AbsListView.CHOICE_MODE_MULTIPLE_MODAL: {
throw new RuntimeException("Multiple Modal not implemented in ItemChoiceManager.");
}
}
}
/**
* Defines the choice behavior for the RecyclerView. By default, RecyclerViewChoiceMode does
* not have any choice behavior (AbsListView.CHOICE_MODE_NONE). By setting the choiceMode to
* AbsListView.CHOICE_MODE_SINGLE, the RecyclerView allows up to one item to be in a
* chosen state.
*
* @param choiceMode One of AbsListView.CHOICE_MODE_NONE, AbsListView.CHOICE_MODE_SINGLE
*/
public void setChoiceMode(int choiceMode) {
if (mChoiceMode != choiceMode) {
mChoiceMode = choiceMode;
clearSelections();
}
}
/**
* Returns the checked state of the specified position. The result is only
* valid if the choice mode has been set to AbsListView.CHOICE_MODE_SINGLE,
* but the code does not check this.
*
* @param position The item whose checked state to return
* @return The item's checked state
* @see #setChoiceMode(int)
*/
public boolean isItemChecked(int position) {
return mCheckStates.get(position);
}
void clearSelections() {
mCheckStates.clear();
mCheckedIdStates.clear();
}
void confirmCheckedPositionsById(int oldItemCount) {
// Clear out the positional check states, we'll rebuild it below from IDs.
mCheckStates.clear();
for (int checkedIndex = 0; checkedIndex < mCheckedIdStates.size(); checkedIndex++) {
final long id = mCheckedIdStates.keyAt(checkedIndex);
final int lastPos = mCheckedIdStates.valueAt(checkedIndex);
final long lastPosId = mAdapter.getItemId(lastPos);
if (id != lastPosId) {
// Look around to see if the ID is nearby. If not, uncheck it.
final int start = Math.max(0, lastPos - CHECK_POSITION_SEARCH_DISTANCE);
final int end = Math.min(lastPos + CHECK_POSITION_SEARCH_DISTANCE, oldItemCount);
boolean found = false;
for (int searchPos = start; searchPos < end; searchPos++) {
final long searchId = mAdapter.getItemId(searchPos);
if (id == searchId) {
found = true;
mCheckStates.put(searchPos, true);
mCheckedIdStates.setValueAt(checkedIndex, searchPos);
break;
}
}
if (!found) {
mCheckedIdStates.delete(id);
checkedIndex--;
}
} else {
mCheckStates.put(lastPos, true);
}
}
}
public void onBindViewHolder(RecyclerView.ViewHolder vh, int position) {
boolean checked = isItemChecked(position);
if (vh.itemView instanceof Checkable) {
((Checkable) vh.itemView).setChecked(checked);
}
ViewCompat.setActivated(vh.itemView, checked);
}
public void onRestoreInstanceState(Bundle savedInstanceState) {
byte[] states = savedInstanceState.getByteArray(SELECTED_ITEMS_KEY);
if ( null != states ) {
Parcel inParcel = Parcel.obtain();
inParcel.unmarshall(states, 0, states.length);
inParcel.setDataPosition(0);
mCheckStates = inParcel.readSparseBooleanArray();
final int numStates = inParcel.readInt();
mCheckedIdStates.clear();
for (int i=0; i<numStates; i++) {
final long key = inParcel.readLong();
final int value = inParcel.readInt();
mCheckedIdStates.put(key, value);
}
}
}
public void onSaveInstanceState(Bundle outState) {
Parcel outParcel = Parcel.obtain();
outParcel.writeSparseBooleanArray(mCheckStates);
final int numStates = mCheckedIdStates.size();
outParcel.writeInt(numStates);
for (int i=0; i<numStates; i++) {
outParcel.writeLong(mCheckedIdStates.keyAt(i));
outParcel.writeInt(mCheckedIdStates.valueAt(i));
}
byte[] states = outParcel.marshall();
outState.putByteArray(SELECTED_ITEMS_KEY, states);
outParcel.recycle();
}
public int getSelectedItemPosition() {
if ( mCheckStates.size() == 0 ) {
return RecyclerView.NO_POSITION;
} else {
return mCheckStates.keyAt(0);
}
}
}

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2015 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.
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/touch_selector_white"
android:elevation="@dimen/appbar_elevation"
android:layout_marginTop="@dimen/abc_list_item_padding_horizontal_material"
>
<include layout="@layout/list_item_base_forecast_today"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</FrameLayout>

View File

@@ -29,7 +29,8 @@
android:elevation="@dimen/appbar_elevation"
android:layout_alignParentTop="true"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
/>
<ImageView
android:id="@+id/sunshine_logo_imageview"
@@ -40,7 +41,8 @@
android:src="@drawable/ic_logo"
android:elevation="@dimen/appbar_elevation"
android:background="@color/primary"
android:contentDescription="@string/app_name"/>
android:contentDescription="@string/app_name"
/>
<!-- This view is used to provide the area that is overlapped
as well as the anchor point that the weather detail will
@@ -70,5 +72,7 @@
android:layout_width="match_parent"
android:layout_below="@id/weather_detail_container"
tools:layout="@android:layout/list_content"
android:choiceMode="singleChoice"
app:autoSelectView="true"
/>
</RelativeLayout>

View File

@@ -79,7 +79,10 @@
android:layout_alignParentStart="true"
android:layout_alignRight="@id/layout_center"
android:layout_below="@id/appbar"
tools:layout="@android:layout/list_content" />
tools:layout="@android:layout/list_content"
android:choiceMode="singleChoice"
app:autoSelectView="true"
/>
<!-- This is used to give the card the appropriate margin
list_item_extra_padding +

View File

@@ -19,4 +19,5 @@
android:name="com.example.android.sunshine.app.ForecastFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout="@android:layout/list_content" />
tools:layout="@android:layout/list_content"
/>

View File

@@ -18,12 +18,13 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:elevation="@dimen/appbar_elevation"
tools:context="com.example.android.sunshine.app.ForecastFragment">
tools:context="com.example.android.sunshine.app.ForecastFragment"
>
<android.support.v7.widget.RecyclerView
style="@style/ForecastListStyle"
android:id="@+id/recyclerview_forecast"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
android:layout_height="match_parent"
/>
<!-- empty list -->
<TextView
android:id="@+id/recyclerview_forecast_empty"

View File

@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2015 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.
-->
<android.support.v7.widget.GridLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:columnCount="2">
<TextView
android:id="@+id/list_item_date_textview"
android:layout_marginBottom="@dimen/abc_list_item_padding_horizontal_material"
android:layout_marginTop="@dimen/abc_list_item_padding_horizontal_material"
android:fontFamily="sans-serif"
android:gravity="center_horizontal"
android:textAppearance="@style/TextAppearance.AppCompat.Title"
android:textColor="@color/secondary_text"
app:layout_columnSpan="2"
app:layout_columnWeight="1"
app:layout_gravity="fill_horizontal"
tools:text="Today, April 03" />
<ImageView
android:id="@+id/list_item_icon"
android:layout_width="0dp"
android:adjustViewBounds="true"
android:maxHeight="@dimen/today_icon"
android:maxWidth="@dimen/today_icon"
app:layout_columnWeight="1"
app:layout_gravity="fill_horizontal"
tools:src="@drawable/art_clouds" />
<TextView
android:id="@+id/list_item_high_textview"
android:layout_width="0dp"
android:fontFamily="sans-serif-light"
android:gravity="center_horizontal"
android:textColor="@color/primary_text"
android:textSize="72sp"
app:layout_columnWeight="1"
app:layout_gravity="fill_horizontal"
tools:text="19" />
<TextView
android:id="@+id/list_item_forecast_textview"
android:layout_width="0dp"
android:fontFamily="sans-serif"
android:gravity="center_horizontal"
android:textAppearance="@style/TextAppearance.AppCompat.Title"
android:textColor="@color/secondary_text"
app:layout_columnWeight="1"
tools:text="Rainy" />
<TextView
android:id="@+id/list_item_low_textview"
android:layout_width="0dp"
android:layout_marginBottom="@dimen/abc_list_item_padding_horizontal_material"
android:fontFamily="sans-serif-light"
android:gravity="center_horizontal"
android:textColor="@color/secondary_text"
android:textSize="36sp"
app:layout_columnWeight="1"
tools:text="10" />
</android.support.v7.widget.GridLayout>

View File

@@ -17,72 +17,13 @@
<!-- Layout for weather forecast list item for today -->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/touch_selector_white"
android:elevation="4dp"
android:elevation="@dimen/appbar_elevation"
>
<android.support.v7.widget.GridLayout
<include layout="@layout/list_item_base_forecast_today"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:columnCount="2">
<TextView
android:id="@+id/list_item_date_textview"
android:layout_marginBottom="@dimen/abc_list_item_padding_horizontal_material"
android:layout_marginTop="@dimen/abc_list_item_padding_horizontal_material"
android:fontFamily="sans-serif"
android:gravity="center_horizontal"
android:textAppearance="@style/TextAppearance.AppCompat.Title"
android:textColor="@color/secondary_text"
app:layout_columnSpan="2"
app:layout_columnWeight="1"
app:layout_gravity="fill_horizontal"
tools:text="Today, April 03" />
<ImageView
android:id="@+id/list_item_icon"
android:layout_width="0dp"
android:adjustViewBounds="true"
android:maxHeight="@dimen/today_icon"
android:maxWidth="@dimen/today_icon"
app:layout_columnWeight="1"
app:layout_gravity="fill_horizontal"
tools:src="@drawable/art_clouds" />
<TextView
android:id="@+id/list_item_high_textview"
android:layout_width="0dp"
android:fontFamily="sans-serif-light"
android:gravity="center_horizontal"
android:textColor="@color/primary_text"
android:textSize="72sp"
app:layout_columnWeight="1"
app:layout_gravity="fill_horizontal"
tools:text="19" />
<TextView
android:id="@+id/list_item_forecast_textview"
android:layout_width="0dp"
android:fontFamily="sans-serif"
android:gravity="center_horizontal"
android:textAppearance="@style/TextAppearance.AppCompat.Title"
android:textColor="@color/secondary_text"
app:layout_columnWeight="1"
tools:text="Rainy" />
<TextView
android:id="@+id/list_item_low_textview"
android:layout_width="0dp"
android:layout_marginBottom="@dimen/abc_list_item_padding_horizontal_material"
android:fontFamily="sans-serif-light"
android:gravity="center_horizontal"
android:textColor="@color/secondary_text"
android:textSize="36sp"
app:layout_columnWeight="1"
tools:text="10" />
</android.support.v7.widget.GridLayout>
/>
</FrameLayout>

View File

@@ -15,7 +15,7 @@
-->
<resources>
<style name="ForecastListStyle">
<style name="ForecastStyle">
<item name="android:choiceMode">singleChoice</item>
</style>

View File

@@ -18,4 +18,8 @@
<declare-styleable name="LocationEditTextPreference">
<attr name="minLength" format="integer" />
</declare-styleable>
<declare-styleable name="ForecastFragment">
<attr name="android:choiceMode" />
<attr name="autoSelectView" format="boolean"/>
</declare-styleable>
</resources>