From 24892e6a9555f36a5d6cbe5658edb6f51ab85eba Mon Sep 17 00:00:00 2001 From: zeapo Date: Sun, 19 Oct 2014 16:45:54 +0200 Subject: [PATCH] Using RecyclerView rather than ListView --- app/build.gradle | 1 + .../com/zeapo/pwdstore/PasswordFragment.java | 178 ++++-------------- .../com/zeapo/pwdstore/PasswordStore.java | 7 +- .../java/com/zeapo/pwdstore/ToCloneOrNot.java | 80 -------- .../zeapo/pwdstore/utils/PasswordAdapter.java | 104 ---------- .../utils/PasswordRecyclerAdapter.java | 97 ++++++++++ app/src/main/res/layout/activity_pwdstore.xml | 4 - .../res/layout/fragment_password_grid.xml | 20 -- .../res/layout/fragment_password_list.xml | 25 --- .../res/layout/password_recycler_view.xml | 16 ++ .../main/res/layout/password_row_layout.xml | 94 +++------ app/src/main/res/values-large/refs.xml | 2 - app/src/main/res/values-sw600dp/refs.xml | 2 - 13 files changed, 178 insertions(+), 452 deletions(-) delete mode 100644 app/src/main/java/com/zeapo/pwdstore/utils/PasswordAdapter.java create mode 100644 app/src/main/java/com/zeapo/pwdstore/utils/PasswordRecyclerAdapter.java delete mode 100644 app/src/main/res/layout/fragment_password_grid.xml delete mode 100644 app/src/main/res/layout/fragment_password_list.xml create mode 100644 app/src/main/res/layout/password_recycler_view.xml diff --git a/app/build.gradle b/app/build.gradle index 059ed531..8521df10 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -30,6 +30,7 @@ repositories { dependencies { compile "com.android.support:appcompat-v7:21.+" + compile "com.android.support:recyclerview-v7:21.+" //compile fileTree(dir: 'libs', include: ['*.jar']) compile project(':libraries:openpgp-api-lib') diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.java b/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.java index 7a327ef2..67e3e858 100644 --- a/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.java +++ b/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.java @@ -1,36 +1,22 @@ package com.zeapo.pwdstore; import android.app.Activity; -import android.content.Context; import android.os.Bundle; import android.app.Fragment; import android.support.v7.app.ActionBarActivity; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; import android.util.Log; -import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.animation.Animation; -import android.view.animation.ScaleAnimation; -import android.widget.AbsListView; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.ExpandableListAdapter; -import android.widget.ExpandableListView; -import android.widget.LinearLayout; -import android.widget.ListAdapter; -import android.widget.ListView; -import com.fortysevendeg.swipelistview.BaseSwipeListViewListener; -import com.fortysevendeg.swipelistview.SwipeListView; -import com.fortysevendeg.swipelistview.SwipeListViewListener; -import com.zeapo.pwdstore.utils.PasswordAdapter; import com.zeapo.pwdstore.utils.PasswordItem; +import com.zeapo.pwdstore.utils.PasswordRecyclerAdapter; import com.zeapo.pwdstore.utils.PasswordRepository; import java.io.File; import java.util.ArrayList; -import java.util.List; import java.util.Stack; /** @@ -40,24 +26,20 @@ import java.util.Stack; * with a GridView. *

*/ -public class PasswordFragment extends Fragment implements SwipeListViewListener{ +public class PasswordFragment extends Fragment{ + + public interface OnFragmentInteractionListener { + public void onFragmentInteraction(PasswordItem item); + } // store the pass files list in a stack private Stack> passListStack; - + private Stack scrollPosition; + private PasswordRecyclerAdapter recyclerAdapter; + private RecyclerView recyclerView; + private RecyclerView.LayoutManager mLayoutManager; private OnFragmentInteractionListener mListener; - /** - * The fragment's ListView/GridView. - */ - private SwipeListView mListView; - - /** - * The Adapter which will be used to populate the ListView/GridView with - * Views. - */ - private PasswordAdapter mAdapter; - /** * Mandatory empty constructor for the fragment manager to instantiate the * fragment (e.g. upon screen orientation changes). @@ -71,19 +53,23 @@ public class PasswordFragment extends Fragment implements SwipeListViewListener{ String path = getArguments().getString("Path"); passListStack = new Stack>(); - - mAdapter = new PasswordAdapter((PasswordStore) getActivity(), PasswordRepository.getPasswords(new File(path))); + scrollPosition = new Stack(); + recyclerAdapter = new PasswordRecyclerAdapter((PasswordStore) getActivity(), mListener, PasswordRepository.getPasswords(new File(path))); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_password, container, false); + View view = inflater.inflate(R.layout.password_recycler_view, container, false); - // Set the adapter - mListView = (SwipeListView) view.findViewById(R.id.pass_list); - ((AdapterView) mListView).setAdapter(mAdapter); - mListView.setSwipeListViewListener(this); + // use a linear layout manager + mLayoutManager = new LinearLayoutManager(getActivity()); + + recyclerView = (RecyclerView) view.findViewById(R.id.pass_recycler); + recyclerView.setLayoutManager(mLayoutManager); +// +// // Set the adapter + recyclerView.setAdapter(recyclerAdapter); return view; } @@ -92,18 +78,20 @@ public class PasswordFragment extends Fragment implements SwipeListViewListener{ super.onAttach(activity); try { mListener = new OnFragmentInteractionListener() { - @Override public void onFragmentInteraction(PasswordItem item) { if (item.getType() == PasswordItem.TYPE_CATEGORY) { - passListStack.push((ArrayList) mAdapter.getValues().clone()); - mAdapter.clear(); - mAdapter.addAll(PasswordRepository.getPasswords(item.getFile())); + passListStack.push((ArrayList) recyclerAdapter.getValues().clone()); + scrollPosition.push(recyclerView.getVerticalScrollbarPosition()); + Log.d("FRAG", scrollPosition.peek() + ""); + recyclerView.scrollToPosition(0); + recyclerAdapter.clear(); + recyclerAdapter.addAll(PasswordRepository.getPasswords(item.getFile())); + ((ActionBarActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true); } } - @Override public void savePosition(Integer position) { } @@ -114,114 +102,22 @@ public class PasswordFragment extends Fragment implements SwipeListViewListener{ } } - @Override - public void onDetach() { - super.onDetach(); - mListener = null; - } - @Override public void onPause() { super.onPause(); - mListener.savePosition(mListView.getFirstVisiblePosition()); - mListView.closeOpenedItems(); - } - - @Override - public void onOpened(int i, boolean b) { - - } - - @Override - public void onClosed(int i, boolean b) { - - } - - @Override - public void onListChanged() { - - } - - @Override - public void onMove(int i, float v) { - - } - - @Override - public void onStartOpen(int i, int i2, boolean b) { - - } - - @Override - public void onStartClose(int i, boolean b) { - - } - - @Override - public void onClickFrontView(int i) { - if (mAdapter.getItem(i).getType() == PasswordItem.TYPE_PASSWORD) { - mListView.openAnimate(i); - } else if (null != mListener) { - // Notify the active callbacks interface (the activity, if the - // fragment is attached to one) that an item has been selected. - mListener.onFragmentInteraction(mAdapter.getItem(i)); - } - } - - @Override - public void onClickBackView(int i) { - mListView.closeAnimate(i); - } - - @Override - public void onDismiss(int[] ints) { - - } - - @Override - public int onChangeSwipeMode(int i) { - return 0; - } - - @Override - public void onChoiceChanged(int i, boolean b) { - - } - - @Override - public void onChoiceStarted() { - - } - - @Override - public void onChoiceEnded() { - - } - - @Override - public void onFirstListItem() { - - } - - @Override - public void onLastListItem() { - - } - - public interface OnFragmentInteractionListener { - public void onFragmentInteraction(PasswordItem item); - public void savePosition(Integer position); +// mListener.savePosition(mListView.getFirstVisiblePosition()); +// mListView.closeOpenedItems(); } public void updateAdapter() { - mAdapter.clear(); - mAdapter.addAll(PasswordRepository.getPasswords(new File(getArguments().getString("Path")))); - mListView.setAdapter((ListAdapter) mAdapter); + recyclerAdapter.clear(); + recyclerAdapter.addAll(PasswordRepository.getPasswords(new File(getArguments().getString("Path")))); } public void popBack() { - mAdapter.clear(); - mAdapter.addAll(passListStack.pop()); + recyclerView.scrollToPosition(scrollPosition.pop()); + recyclerAdapter.clear(); + recyclerAdapter.addAll(passListStack.pop()); } public boolean isNotEmpty() { diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordStore.java b/app/src/main/java/com/zeapo/pwdstore/PasswordStore.java index 2ce7120e..25200c92 100644 --- a/app/src/main/java/com/zeapo/pwdstore/PasswordStore.java +++ b/app/src/main/java/com/zeapo/pwdstore/PasswordStore.java @@ -30,7 +30,7 @@ import java.util.ArrayList; import java.util.Stack; -public class PasswordStore extends ActionBarActivity implements ToCloneOrNot.OnFragmentInteractionListener { +public class PasswordStore extends ActionBarActivity { private Stack scrollPositions; /** if we leave the activity to do something, do not add any other fragment */ public boolean leftActivity = false; @@ -231,11 +231,6 @@ public class PasswordStore extends ActionBarActivity implements ToCloneOrNot.OnF } } - @Override - public void onFragmentInteraction(Uri uri) { - - } - private void checkLocalRepository() { checkLocalRepository(PasswordRepository.getWorkTree()); } diff --git a/app/src/main/java/com/zeapo/pwdstore/ToCloneOrNot.java b/app/src/main/java/com/zeapo/pwdstore/ToCloneOrNot.java index dd1ebda1..38d0a5d1 100644 --- a/app/src/main/java/com/zeapo/pwdstore/ToCloneOrNot.java +++ b/app/src/main/java/com/zeapo/pwdstore/ToCloneOrNot.java @@ -9,45 +9,8 @@ import android.view.View; import android.view.ViewGroup; - -/** - * A simple {@link Fragment} subclass. - * Activities that contain this fragment must implement the - * {@link ToCloneOrNot.OnFragmentInteractionListener} interface - * to handle interaction events. - * Use the {@link ToCloneOrNot#newInstance} factory method to - * create an instance of this fragment. - * - */ public class ToCloneOrNot extends Fragment { - // TODO: Rename parameter arguments, choose names that match - // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER - private static final String ARG_PARAM1 = "param1"; - private static final String ARG_PARAM2 = "param2"; - // TODO: Rename and change types of parameters - private String mParam1; - private String mParam2; - - private OnFragmentInteractionListener mListener; - - /** - * Use this factory method to create a new instance of - * this fragment using the provided parameters. - * - * @param param1 Parameter 1. - * @param param2 Parameter 2. - * @return A new instance of fragment ToCloneOrNot. - */ - // TODO: Rename and change types and number of parameters - public static ToCloneOrNot newInstance(String param1, String param2) { - ToCloneOrNot fragment = new ToCloneOrNot(); - Bundle args = new Bundle(); - args.putString(ARG_PARAM1, param1); - args.putString(ARG_PARAM2, param2); - fragment.setArguments(args); - return fragment; - } public ToCloneOrNot() { // Required empty public constructor } @@ -55,10 +18,6 @@ public class ToCloneOrNot extends Fragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (getArguments() != null) { - mParam1 = getArguments().getString(ARG_PARAM1); - mParam2 = getArguments().getString(ARG_PARAM2); - } } @Override @@ -68,43 +27,4 @@ public class ToCloneOrNot extends Fragment { return inflater.inflate(R.layout.fragment_to_clone_or_not, container, false); } - // TODO: Rename method, update argument and hook method into UI event - public void onButtonPressed(Uri uri) { - if (mListener != null) { - mListener.onFragmentInteraction(uri); - } - } - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - try { - mListener = (OnFragmentInteractionListener) activity; - } catch (ClassCastException e) { - throw new ClassCastException(activity.toString() - + " must implement OnFragmentInteractionListener"); - } - } - - @Override - public void onDetach() { - super.onDetach(); - mListener = null; - } - - /** - * This interface must be implemented by activities that contain this - * fragment to allow an interaction in this fragment to be communicated - * to the activity and potentially other fragments contained in that - * activity. - *

- * See the Android Training lesson Communicating with Other Fragments for more information. - */ - public interface OnFragmentInteractionListener { - // TODO: Update argument type and name - public void onFragmentInteraction(Uri uri); - } - } diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/PasswordAdapter.java b/app/src/main/java/com/zeapo/pwdstore/utils/PasswordAdapter.java deleted file mode 100644 index e3317c01..00000000 --- a/app/src/main/java/com/zeapo/pwdstore/utils/PasswordAdapter.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.zeapo.pwdstore.utils; - -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.database.DataSetObserver; -import android.graphics.Typeface; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AbsListView; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.ExpandableListAdapter; -import android.widget.GridLayout; -import android.widget.ImageButton; -import android.widget.TextView; - -import com.zeapo.pwdstore.PasswordStore; -import com.zeapo.pwdstore.R; -import com.zeapo.pwdstore.crypto.PgpHandler; - -import org.apache.commons.io.FileUtils; - -import java.util.ArrayList; - -public class PasswordAdapter extends ArrayAdapter{ - private final PasswordStore activity; - private final ArrayList values; - - static class ViewHolder { - public TextView name; - public TextView type; - public TextView back_name; - } - - public PasswordAdapter(PasswordStore activity, ArrayList values) { - super(activity, R.layout.password_row_layout, values); - this.values = values; - this.activity = activity; - } - - public ArrayList getValues() { - return values; - } - - @Override - public View getView(int i, View convertView, ViewGroup viewGroup) { - View rowView = convertView; - final PasswordItem pass = values.get(i); - - // reuse for performance, holder pattern! - if (rowView == null) { - LayoutInflater inflater = (LayoutInflater) activity - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - rowView = inflater.inflate(R.layout.password_row_layout, viewGroup, false); - - ViewHolder viewHolder = new ViewHolder(); - viewHolder.name = (TextView) rowView.findViewById(R.id.label); - viewHolder.back_name = (TextView) rowView.findViewById(R.id.label_back); - viewHolder.type = (TextView) rowView.findViewById(R.id.type); - rowView.setTag(viewHolder); - } - - ViewHolder holder = (ViewHolder) rowView.getTag(); - - holder.name.setText(pass.toString()); - holder.back_name.setText(pass.toString()); - - if (pass.getType() == PasswordItem.TYPE_CATEGORY) { - holder.name.setTextColor(this.activity.getResources().getColor(android.R.color.holo_blue_dark)); - holder.name.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD)); - holder.type.setText("Category: "); - } else { - holder.type.setText("Password: "); - holder.name.setTextColor(this.activity.getResources().getColor(android.R.color.holo_orange_dark)); - holder.name.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.NORMAL)); - - holder.back_name.setTextColor(this.activity.getResources().getColor(android.R.color.white)); - holder.back_name.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD_ITALIC)); - - - View.OnClickListener onClickListener = new View.OnClickListener() { - @Override - public void onClick(View view) { - switch (view.getId()) { - case R.id.crypto_show_button: - activity.decryptPassword(pass); - break; - case R.id.crypto_delete_button: - activity.deletePassword(pass); - break; - } - } - }; - - ((ImageButton) rowView.findViewById(R.id.crypto_show_button)).setOnClickListener(onClickListener); - ((ImageButton) rowView.findViewById(R.id.crypto_delete_button)).setOnClickListener(onClickListener); - } - - return rowView; - } -} \ No newline at end of file diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/PasswordRecyclerAdapter.java b/app/src/main/java/com/zeapo/pwdstore/utils/PasswordRecyclerAdapter.java new file mode 100644 index 00000000..47bcb41b --- /dev/null +++ b/app/src/main/java/com/zeapo/pwdstore/utils/PasswordRecyclerAdapter.java @@ -0,0 +1,97 @@ +package com.zeapo.pwdstore.utils; + +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.zeapo.pwdstore.PasswordFragment; +import com.zeapo.pwdstore.PasswordStore; +import com.zeapo.pwdstore.R; + +import java.util.ArrayList; + +public class PasswordRecyclerAdapter extends RecyclerView.Adapter { + private final PasswordStore activity; + private final ArrayList values; + private final PasswordFragment.OnFragmentInteractionListener listener; + + // Provide a reference to the views for each data item + // Complex data items may need more than one view per item, and + // you provide access to all the views for a data item in a view holder + public static class ViewHolder extends RecyclerView.ViewHolder { + // each data item is just a string in this case + public View view; + public TextView name; + public TextView type; + public int position; + + public ViewHolder(View v) { + super(v); + view = v; + name = (TextView) view.findViewById(R.id.label); + type = (TextView) view.findViewById(R.id.type); + } + } + + // Provide a suitable constructor (depends on the kind of dataset) + public PasswordRecyclerAdapter(PasswordStore activity, PasswordFragment.OnFragmentInteractionListener listener, ArrayList values) { + this.values = values; + this.activity = activity; + this.listener = listener; + } + + // Create new views (invoked by the layout manager) + @Override + public PasswordRecyclerAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, + int viewType) { + // create a new view + View v = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.password_row_layout, parent, false); + ViewHolder vh = new ViewHolder(v); + return vh; + } + + // Replace the contents of a view (invoked by the layout manager) + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + final PasswordItem pass = values.get(position); + holder.name.setText(pass.getName()); + holder.type.setText((pass.getType() == PasswordItem.TYPE_CATEGORY ? "Category" : "Password")); + holder.position = position; + + holder.view.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + listener.onFragmentInteraction(pass); + } + }); + } + + // Return the size of your dataset (invoked by the layout manager) + @Override + public int getItemCount() { + return values.size(); + } + + public ArrayList getValues() { + return this.values; + } + + public void clear() { + this.values.clear(); + this.notifyDataSetChanged(); + } + + public void addAll(ArrayList list) { + this.values.addAll(list); + this.notifyDataSetChanged(); + } + + public void add(PasswordItem item) { + this.values.add(item); + this.notifyDataSetChanged(); + } + +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_pwdstore.xml b/app/src/main/res/layout/activity_pwdstore.xml index 1e6f1e41..b7074083 100644 --- a/app/src/main/res/layout/activity_pwdstore.xml +++ b/app/src/main/res/layout/activity_pwdstore.xml @@ -2,10 +2,6 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingLeft="@dimen/activity_horizontal_margin" - android:paddingRight="@dimen/activity_horizontal_margin" - android:paddingTop="@dimen/activity_vertical_margin" - android:paddingBottom="@dimen/activity_vertical_margin" android:background="#eee" tools:context=".pwdstore" android:orientation="vertical"> diff --git a/app/src/main/res/layout/fragment_password_grid.xml b/app/src/main/res/layout/fragment_password_grid.xml deleted file mode 100644 index b23aa4d3..00000000 --- a/app/src/main/res/layout/fragment_password_grid.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/layout/fragment_password_list.xml b/app/src/main/res/layout/fragment_password_list.xml deleted file mode 100644 index 43a8a91b..00000000 --- a/app/src/main/res/layout/fragment_password_list.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - diff --git a/app/src/main/res/layout/password_recycler_view.xml b/app/src/main/res/layout/password_recycler_view.xml new file mode 100644 index 00000000..c50f5952 --- /dev/null +++ b/app/src/main/res/layout/password_recycler_view.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/main/res/layout/password_row_layout.xml b/app/src/main/res/layout/password_row_layout.xml index 05bf3457..3636a20b 100644 --- a/app/src/main/res/layout/password_row_layout.xml +++ b/app/src/main/res/layout/password_row_layout.xml @@ -1,71 +1,29 @@ - + - - - - - - - - - - - - - - - - + android:padding="8dp" + android:textStyle="bold" + android:layout_gravity="center_vertical" + /> + + \ No newline at end of file diff --git a/app/src/main/res/values-large/refs.xml b/app/src/main/res/values-large/refs.xml index 975e10fa..91cab110 100644 --- a/app/src/main/res/values-large/refs.xml +++ b/app/src/main/res/values-large/refs.xml @@ -9,6 +9,4 @@ http://developer.android.com/training/multiscreen/screensizes.html#TaskUseAliasFilters --> - @layout/fragment_password_grid - \ No newline at end of file diff --git a/app/src/main/res/values-sw600dp/refs.xml b/app/src/main/res/values-sw600dp/refs.xml index 975e10fa..91cab110 100644 --- a/app/src/main/res/values-sw600dp/refs.xml +++ b/app/src/main/res/values-sw600dp/refs.xml @@ -9,6 +9,4 @@ http://developer.android.com/training/multiscreen/screensizes.html#TaskUseAliasFilters --> - @layout/fragment_password_grid - \ No newline at end of file