Added selection into forecast adapter.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,10 +200,13 @@ 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)) {
|
||||
// The Recycler View probably hasn't even been populated yet. Actually perform the
|
||||
// swapout in onLoadFinished.
|
||||
mPosition = savedInstanceState.getInt(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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
29
app/src/main/res/layout-land/list_item_forecast_today.xml
Normal file
29
app/src/main/res/layout-land/list_item_forecast_today.xml
Normal 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>
|
||||
@@ -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>
|
||||
@@ -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 +
|
||||
|
||||
@@ -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"
|
||||
/>
|
||||
|
||||
@@ -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"
|
||||
|
||||
80
app/src/main/res/layout/list_item_base_forecast_today.xml
Normal file
80
app/src/main/res/layout/list_item_base_forecast_today.xml
Normal 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>
|
||||
@@ -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>
|
||||
@@ -15,7 +15,7 @@
|
||||
-->
|
||||
<resources>
|
||||
|
||||
<style name="ForecastListStyle">
|
||||
<style name="ForecastStyle">
|
||||
<item name="android:choiceMode">singleChoice</item>
|
||||
</style>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user