package com.afollestad.silk.fragments;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.afollestad.silk.R;
import com.afollestad.silk.adapters.SilkAdapter;
import com.afollestad.silk.caching.SilkComparable;
/**
* A {@link com.afollestad.silk.fragments.SilkFragment} that shows a list, with an empty text, and has progress bar support. Has other various
* convenience methods and handles a lot of things on its own to make things easy.
* <p/>
* The fragment uses a {@link com.afollestad.silk.adapters.SilkAdapter} to display items of type ItemType.
*
* @param <ItemType> The type of items held in the fragment's list.
* @author Aidan Follestad (afollestad)
*/
public abstract class SilkListFragment<ItemType extends SilkComparable> extends SilkFragment {
private AbsListView mListView;
private TextView mEmpty;
private ProgressBar mProgress;
private SilkAdapter<ItemType> mAdapter;
private boolean mLoading;
/**
* Gets the ListView contained in the Fragment's layout.
*/
public final AbsListView getListView() {
return mListView;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAdapter = initializeAdapter();
if (mAdapter == null) throw new RuntimeException("The SilkListFragment's adapter cannot be null.");
}
/**
* Uses a list layout by default but this can be overridden if necessary. If you do override this method,
* the returned layout must have the same views with the same IDs in addition to whatever you add or change.
*/
@Override
protected int getLayout() {
return R.layout.fragment_list;
}
@Override
public String getTitle() {
// This isn't needed but can be overridden by inheriting classes if needed.
return null;
}
/**
* Inheriting classes return a string resource for the list's empty text value here.
* <p/>
* The text will be shown when the list is not loading and the list is empty.
*/
protected abstract int getEmptyText();
/**
* Updates the edit text that was initially set to the value of {@link #getEmptyText()}.
*/
public final void setEmptyText(CharSequence text) {
mEmpty.setText(text);
}
/**
* Gets the SilkAdapter used to add and remove items from the list.
*/
public final SilkAdapter<ItemType> getAdapter() {
return mAdapter;
}
/**
* Causes the fragment's adapter to be recreated.
*/
public final void recreateAdapter() {
mAdapter = initializeAdapter();
}
/**
* Only called once to cause inheriting classes to create a new SilkAdapter that can later be retrieved using
* {#getAdapter}.
*/
protected abstract SilkAdapter<ItemType> initializeAdapter();
/**
* Called when an item in the list is tapped by the user.
*
* @param index The index of the tapped item.
* @param item The actual tapped item from the adapter.
* @param view The view in the list that was tapped.
*/
protected abstract void onItemTapped(int index, ItemType item, View view);
/**
* Called when an item in the list is long-tapped by the user.
*
* @param index The index of the long-tapped item.
* @param item The actual long-tapped item from the adapter.
* @param view The view in the list that was long-tapped.
* @return Whether or not the event was handled.
*/
protected abstract boolean onItemLongTapped(int index, ItemType item, View view);
/**
* Gets whether or not the list is currently loading.
* <p/>
* This value is changed using {#setLoading} and {#setLoadComplete}.
*/
public final boolean isLoading() {
return mLoading;
}
private void setListShown(boolean shown) {
mListView.setVisibility(shown ? View.VISIBLE : View.GONE);
if (!shown) {
if (mEmpty != null) mEmpty.setVisibility(View.GONE);
} else {
mListView.setEmptyView(mEmpty);
getAdapter().notifyDataSetChanged();
}
if (mProgress != null) mProgress.setVisibility(shown ? View.GONE : View.VISIBLE);
}
/**
* Notifies the fragment that it is currently loading data.
* <p/>
* If true is passed as a parameter, the list or empty text will be hidden, and the progress view to be shown.
*
* @param progress Whether or not the progress view will be shown and the list will be hidden.
*/
public final void setLoading(boolean progress) {
if (progress)
setListShown(false);
mLoading = true;
}
/**
* Notifies the fragment that it is done loading data. This causes the progress view to become invisible, and the list
* or empty text become visible again.
*
* @param error Whether or not an error occurred while loading. This value is not used in the default implementation
* but can be used by overriding classes.
*/
public final void setLoadComplete(boolean error) {
mLoading = false;
setListShown(true);
}
/**
* References to views are created here, along with hooks to event handlers. If you override this method in a sub-class,
* make sure you make a call to the super method.
*/
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mListView = (AbsListView) view.findViewById(android.R.id.list);
mEmpty = (TextView) view.findViewById(android.R.id.empty);
mProgress = (ProgressBar) view.findViewById(android.R.id.progress);
if (mListView == null)
throw new RuntimeException(getClass().getName() + ": your list fragment layout must contain a ListView with the ID @android:id/list.");
if (mEmpty == null)
Log.w(getClass().getName(), "Warning: no empty view with ID @android:id/empty found in list fragment layout.");
if (mProgress == null)
Log.w(getClass().getName(), "Warning: no progress view with ID @android:id/progress found in list fragment layout.");
mListView.setAdapter(mAdapter);
mListView.setEmptyView(mEmpty);
if (mEmpty != null && getEmptyText() > 0)
mEmpty.setText(getEmptyText());
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int index, long id) {
ItemType item = getAdapter().getItem(index);
onItemTapped(index, item, view);
}
});
mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int index, long id) {
ItemType item = getAdapter().getItem(index);
return onItemLongTapped(index, item, view);
}
});
}
}