New list with sticky list headers library

This commit is contained in:
Dominik Schürmann
2014-01-02 21:10:08 +01:00
parent 1d91804dc7
commit f5da63f988
63 changed files with 3687 additions and 61 deletions

View File

@@ -24,6 +24,9 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.ui.adapter.KeyListPublicAdapter;
import se.emilsjolander.stickylistheaders.ApiLevelTooLowException;
import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
@@ -31,58 +34,84 @@ import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import com.actionbarsherlock.app.SherlockListFragment;
import com.actionbarsherlock.app.SherlockFragment;
public class KeyListPublicFragment extends SherlockListFragment implements
LoaderManager.LoaderCallbacks<Cursor> {
/**
* Public key list with sticky list headers.
*
* - uses StickyListHeaders library
* - custom adapter: KeyListPublicAdapter
*
* TODO:
* - fix loader with spinning animation
* - fix design
* - fix view holder in adapter
*
*/
public class KeyListPublicFragment extends SherlockFragment implements
AdapterView.OnItemClickListener, LoaderManager.LoaderCallbacks<Cursor> {
private KeyListPublicActivity mKeyListPublicActivity;
private KeyListPublicAdapter mAdapter;
StickyListHeadersListView stickyList;
/**
* Define Adapter and Loader on create of Activity
*/
@SuppressLint("NewApi")
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mKeyListPublicActivity = (KeyListPublicActivity) getActivity();
getListView().setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
// start key view on click
Intent detailsIntent = new Intent(mKeyListPublicActivity, KeyViewActivity.class);
detailsIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsUri(Long
.toString(id)));
startActivity(detailsIntent);
}
});
stickyList = (StickyListHeadersListView) getActivity().findViewById(
R.id.key_list_public_fragment_stickylist);
stickyList.setOnItemClickListener(this);
// stickyList.setOnHeaderClickListener(this);
// stickyList.setOnStickyHeaderOffsetChangedListener(this);
// mStickyList.addHeaderView(inflater.inflate(R.layout.list_header, null));
// mStickyList.addFooterView(inflater.inflate(R.layout.list_footer, null));
// stickyList.setEmptyView(findViewById(R.id.empty));
stickyList.setAreHeadersSticky(true);
stickyList.setDrawingListUnderStickyHeader(true);
stickyList.setFastScrollEnabled(true);
try {
stickyList.setFastScrollAlwaysVisible(true);
} catch (ApiLevelTooLowException e) {
}
// Give some text to display if there is no data. In a real
// application this would come from a resource.
setEmptyText(getString(R.string.list_empty));
// We have a menu item to show in action bar.
setHasOptionsMenu(true);
// setEmptyText(getString(R.string.list_empty));
// Start out with a progress indicator.
setListShown(false);
// setListShown(false);
// Create an empty adapter we will use to display the loaded data.
mAdapter = new KeyListPublicAdapter(mKeyListPublicActivity, null, Id.type.public_key);
setListAdapter(mAdapter);
// mAdapter = new KeyListPublicAdapter(mKeyListPublicActivity, null, Id.type.public_key);
// setListAdapter(mAdapter);
// stickyList.setAdapter(mAdapter);
// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
getLoaderManager().initLoader(0, null, this);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.key_list_public_fragment, container, false);
return view;
}
// These are the rows that we will retrieve.
static final String[] PROJECTION = new String[] { KeyRings._ID, KeyRings.MASTER_KEY_ID,
UserIds.USER_ID };
@@ -104,13 +133,19 @@ public class KeyListPublicFragment extends SherlockListFragment implements
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
mAdapter.swapCursor(data);
// mAdapter.swapCursor(data);
int userIdIndex = data.getColumnIndex(UserIds.USER_ID);
mAdapter = new KeyListPublicAdapter(mKeyListPublicActivity, data, Id.type.public_key,
userIdIndex);
stickyList.setAdapter(mAdapter);
// The list should now be shown.
if (isResumed()) {
setListShown(true);
// setListShown(true);
} else {
setListShownNoAnimation(true);
// setListShownNoAnimation(true);
}
}
@@ -122,4 +157,12 @@ public class KeyListPublicFragment extends SherlockListFragment implements
mAdapter.swapCursor(null);
}
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
// start key view on click
Intent detailsIntent = new Intent(mKeyListPublicActivity, KeyViewActivity.class);
detailsIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsUri(Long.toString(id)));
startActivity(detailsIntent);
}
}

View File

@@ -17,30 +17,40 @@
package org.sufficientlysecure.keychain.ui.adapter;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.util.SectionCursorAdapter;
import org.sufficientlysecure.keychain.util.Log;
import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
import android.content.Context;
import android.database.Cursor;
import android.support.v4.widget.CursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class KeyListPublicAdapter extends SectionCursorAdapter {
/**
* - implements StickyListHeadersAdapter from library - uses view holder pattern for performance
*
*/
public class KeyListPublicAdapter extends CursorAdapter implements StickyListHeadersAdapter {
private LayoutInflater mInflater;
public KeyListPublicAdapter(Context context, Cursor c, int flags) {
super(context, c, android.R.layout.preference_category, 2); // TODO: 2 is user id
int mSectionColumnIndex;
public KeyListPublicAdapter(Context context, Cursor c, int flags, int sectionColumnIndex) {
super(context, c, flags);
mInflater = LayoutInflater.from(context);
mSectionColumnIndex = sectionColumnIndex;
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
// TODO: view holder pattern?
int userIdIndex = cursor.getColumnIndex(UserIds.USER_ID);
TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
@@ -74,4 +84,64 @@ public class KeyListPublicAdapter extends SectionCursorAdapter {
return mInflater.inflate(R.layout.key_list_group_item, null);
}
@Override
public View getHeaderView(int position, View convertView, ViewGroup parent) {
HeaderViewHolder holder;
if (convertView == null) {
holder = new HeaderViewHolder();
convertView = mInflater.inflate(R.layout.stickylist_header, parent, false);
holder.text = (TextView) convertView.findViewById(R.id.stickylist_header_text);
convertView.setTag(holder);
} else {
holder = (HeaderViewHolder) convertView.getTag();
}
if (!mDataValid) {
// no data available at this point
Log.d(Constants.TAG, "getHeaderView: No data available at this point!");
return convertView;
}
// similar to getView in CursorAdapter
if (!mCursor.moveToPosition(position)) {
throw new IllegalStateException("couldn't move cursor to position " + position);
}
// set header text as first char in name
String headerText = "" + mCursor.getString(mSectionColumnIndex).subSequence(0, 1).charAt(0);
holder.text.setText(headerText);
return convertView;
}
/**
* Remember that these have to be static, position=1 should always return the same Id that is.
*/
@Override
public long getHeaderId(int position) {
if (!mDataValid) {
// no data available at this point
Log.d(Constants.TAG, "getHeaderView: No data available at this point!");
return -1;
}
// similar to getView in CursorAdapter
if (!mCursor.moveToPosition(position)) {
throw new IllegalStateException("couldn't move cursor to position " + position);
}
// return the first character of the name as ID because this is what
// headers are based upon
return mCursor.getString(mSectionColumnIndex).subSequence(0, 1).charAt(0);
}
class HeaderViewHolder {
TextView text;
}
class ViewHolder {
TextView mainUserId;
TextView mainUserIdRest;
}
}

View File

@@ -0,0 +1,50 @@
package se.emilsjolander.stickylistheaders.views;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.TextView;
/**
* @author Eric Frohnhoefer
*/
public class UnderlineTextView extends TextView {
private final Paint mPaint = new Paint();
private int mUnderlineHeight = 0;
public UnderlineTextView(Context context) {
this(context, null);
}
public UnderlineTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public UnderlineTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
Resources r = getResources();
mUnderlineHeight = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, r.getDisplayMetrics());
}
@Override
public void setPadding(int left, int top, int right, int bottom) {
super.setPadding(left, top, right, bottom + mUnderlineHeight);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Draw the underline the same color as the text
mPaint.setColor(getTextColors().getDefaultColor());
canvas.drawRect(0, getHeight() - mUnderlineHeight, getWidth(), getHeight(), mPaint);
}
}