Initial interface development
This commit is contained in:
parent
feee5f20e9
commit
501a3f80ca
|
@ -10,13 +10,11 @@ import android.util.Log;
|
|||
import hikapro.com.backpack.model.DetailModel;
|
||||
import hikapro.com.backpack.model.ItemModel;
|
||||
import hikapro.com.backpack.model.SetModel;
|
||||
import hikapro.com.backpack.model.database.DAO;
|
||||
import hikapro.com.backpack.model.entities.Item;
|
||||
import hikapro.com.backpack.model.entities.Set;
|
||||
import hikapro.com.backpack.presenter.ItemDetailPresenter;
|
||||
import hikapro.com.backpack.presenter.ItemListPresenter;
|
||||
import hikapro.com.backpack.presenter.SetListPresenter;
|
||||
import hikapro.com.backpack.presenter.adapters.helper.OnStartDragListener;
|
||||
import hikapro.com.backpack.view.View;
|
||||
import hikapro.com.backpack.view.fragments.ItemDetailFragment;
|
||||
import hikapro.com.backpack.view.fragments.ItemListFragment;
|
||||
|
|
|
@ -9,16 +9,12 @@ import java.util.Collections;
|
|||
import java.util.Hashtable;
|
||||
import java.util.List;
|
||||
|
||||
import hikapro.com.backpack.model.database.Command;
|
||||
import hikapro.com.backpack.model.database.DAO;
|
||||
import hikapro.com.backpack.model.database.Event;
|
||||
import hikapro.com.backpack.model.dao.Command;
|
||||
import hikapro.com.backpack.model.dao.DAO;
|
||||
import hikapro.com.backpack.model.dao.Event;
|
||||
import hikapro.com.backpack.model.entities.Category;
|
||||
import hikapro.com.backpack.model.entities.Item;
|
||||
import hikapro.com.backpack.model.entities.Set;
|
||||
import hikapro.com.backpack.presenter.Presenter;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
/**
|
||||
* Created by tariel on 22/04/16.
|
||||
|
@ -29,19 +25,25 @@ public class ItemModel implements Model.Item {
|
|||
private List<Category> rawCategories;
|
||||
private List<Category> sortedCategories;
|
||||
private List<Item> rawItems;
|
||||
|
||||
private DAO dao;
|
||||
private int currentSet;
|
||||
private Category currentCategory;
|
||||
private List<Category> categoriesCache;
|
||||
private Hashtable<Integer, Category> categoriesCache;
|
||||
private List<Item> itemsCache;
|
||||
private List<Item> itemsDiscardCache;
|
||||
|
||||
private Hashtable<Category, List<Item>> items;
|
||||
private Hashtable<Integer, Hashtable<Category, List<Item>>> cache;
|
||||
private Hashtable<Integer, List<Item>> cache;
|
||||
|
||||
public ItemModel() {
|
||||
this.rawCategories = new ArrayList<>();
|
||||
this.rawItems = new ArrayList<>();
|
||||
this.sortedCategories = new ArrayList<>();
|
||||
this.categoriesCache = new ArrayList<>(20);
|
||||
|
||||
this.categoriesCache = new Hashtable<>(20, 0.9f);
|
||||
|
||||
this.itemsCache = new ArrayList<>();
|
||||
this.itemsDiscardCache = new ArrayList<>();
|
||||
this.cache = new Hashtable<>(12, 0.9f);
|
||||
this.dao = DAO.getInstance();
|
||||
dao.registerObserver(this);
|
||||
|
@ -51,8 +53,11 @@ public class ItemModel implements Model.Item {
|
|||
|
||||
@Override
|
||||
public Category getCategoryByPosition(int position) {
|
||||
currentCategory = categoriesCache.get(position);
|
||||
return currentCategory;
|
||||
Category ret = null;
|
||||
if (cache.containsKey(currentSet))
|
||||
ret = categoriesCache.get(cache.get(currentSet).get(position).getCategory());
|
||||
return ret;
|
||||
|
||||
}
|
||||
@Override
|
||||
public int getCategoriesCount() {
|
||||
|
@ -65,12 +70,23 @@ public class ItemModel implements Model.Item {
|
|||
|
||||
@Override
|
||||
public int insertItem(Item item) {
|
||||
List<Item> iList = cache.get(currentSet);
|
||||
iList.add(item);
|
||||
Collections.sort(iList);
|
||||
cache.put(currentSet, iList);
|
||||
// TODO write to database
|
||||
return 0;
|
||||
}
|
||||
@Override
|
||||
public boolean deleteItem(int id) {
|
||||
public boolean deleteItem(Item item) {
|
||||
if (isPendingRemoval(item))
|
||||
itemsDiscardCache.remove(item);
|
||||
cache.get(currentSet).remove(item);
|
||||
//TODO write to ds
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item findItem(int id) {
|
||||
Item item = null;
|
||||
|
@ -83,15 +99,75 @@ public class ItemModel implements Model.Item {
|
|||
return item;
|
||||
}
|
||||
@Override
|
||||
public Item getItemByPosition(int categoryId, int position) {
|
||||
Item ret = cache.get(currentSet).get(currentCategory).get(position);
|
||||
public Item getItemByPosition(int position) {
|
||||
Item ret = null;
|
||||
if (cache.containsKey(currentSet))
|
||||
ret = cache.get(currentSet).get(position);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getItemsCount(int categoryId) {
|
||||
int ret = cache.get(currentSet).get(currentCategory).size();
|
||||
return ret;
|
||||
public void filter(String query) {
|
||||
|
||||
if (query.isEmpty()) {
|
||||
Message command = Message.obtain();
|
||||
command.what = Command.SET_GET_ITEMS;
|
||||
command.arg1 = presenter.getCurrentSet().getId();
|
||||
dao.executeCommand(command);
|
||||
} else {
|
||||
query = query.toLowerCase();
|
||||
String name;
|
||||
List<Item> newList = new ArrayList<>(20);
|
||||
List<Item> oldList = cache.get(currentSet);
|
||||
for (Item item : oldList) {
|
||||
name = item.getName().toLowerCase();
|
||||
if (name.contains(query)) {
|
||||
newList.add(item);
|
||||
}
|
||||
}
|
||||
cache.put(currentSet, newList);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeaderId(int position) {
|
||||
return cache.containsKey(currentSet) ? cache.get(currentSet).get(position).getCategory() : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemId(int position) {
|
||||
return cache.containsKey(currentSet) ? cache.get(currentSet).get(position).getId() : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
if (cache.containsKey(currentSet))
|
||||
cache.get(currentSet).clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPendingRemoval(Item item) {
|
||||
return itemsDiscardCache.contains(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pendingRemoveCancel(Item item) {
|
||||
if (itemsDiscardCache.contains(item))
|
||||
itemsDiscardCache.remove(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pendingRemove(Item item) {
|
||||
itemsDiscardCache.add(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemsCount() {
|
||||
boolean is = cache.containsKey(currentSet);
|
||||
return is ? cache.get(currentSet).size() : 0;
|
||||
}
|
||||
|
||||
// items <--
|
||||
|
||||
// events -->
|
||||
|
@ -117,17 +193,21 @@ public class ItemModel implements Model.Item {
|
|||
|
||||
@Override
|
||||
public void executeQuery() {
|
||||
Message command;
|
||||
|
||||
if (cache.contains(currentSet)) {
|
||||
Hashtable<Category, List<Item>> buff = cache.get(currentSet);
|
||||
Category[] array = buff.keySet().toArray(new Category[buff.keySet().size()]);
|
||||
categoriesCache = Arrays.asList(array);
|
||||
notifyDataSetChanged();
|
||||
} else {
|
||||
Message command = Message.obtain();
|
||||
if (categoriesCache.isEmpty()) {
|
||||
command = Message.obtain();
|
||||
command.what = Command.ITEM_GET_CATEGORIES;
|
||||
dao.executeCommand(command);
|
||||
}
|
||||
command = Message.obtain();
|
||||
command.what = Command.SET_GET_ITEMS;
|
||||
command.arg1 = presenter.getCurrentSet().getId();
|
||||
dao.executeCommand(command);
|
||||
|
||||
}
|
||||
}
|
||||
@Override
|
||||
|
@ -146,13 +226,16 @@ public class ItemModel implements Model.Item {
|
|||
break;
|
||||
case Event.ITEM_INSERT_ERROR :
|
||||
break;
|
||||
case Event.ITEM_CATEGORY_LOAD_ERROR :
|
||||
break;
|
||||
case Event.SET_ITEMS_LOAD_COMPLETED :
|
||||
Hashtable<Category, List<Item>> res = (Hashtable<Category, List<Item>>) event.obj;
|
||||
List<Item> res = (List<Item>) event.obj;
|
||||
cache.put(event.arg1, res);
|
||||
Category[] array = res.keySet().toArray(new Category[res.keySet().size()]);
|
||||
categoriesCache = Arrays.asList(array);
|
||||
notifyDataSetChanged();
|
||||
break;
|
||||
case Event.ITEM_CATEGORY_LOAD_COMPLETED :
|
||||
categoriesCache = (Hashtable<Integer, Category>)event.obj;
|
||||
break;
|
||||
case Event.ITEM_FROM_SET_DELETED :
|
||||
break;
|
||||
case Event.ITEM_DELETED :
|
||||
|
@ -220,7 +303,6 @@ public class ItemModel implements Model.Item {
|
|||
}
|
||||
}
|
||||
return category;
|
||||
|
||||
}
|
||||
private Category findCategory(int categoryId) {
|
||||
Category category = null;
|
||||
|
|
|
@ -34,10 +34,20 @@ public interface Model {
|
|||
|
||||
interface Item extends Base {
|
||||
int insertItem(hikapro.com.backpack.model.entities.Item item);
|
||||
boolean deleteItem(int id);
|
||||
boolean deleteItem(hikapro.com.backpack.model.entities.Item item);
|
||||
void filter(String query);
|
||||
int getHeaderId(int position);//TODO review
|
||||
int getItemId(int position);//TODO review
|
||||
void clear();
|
||||
boolean isPendingRemoval(hikapro.com.backpack.model.entities.Item item);
|
||||
void pendingRemove(hikapro.com.backpack.model.entities.Item item);
|
||||
void pendingRemoveCancel(hikapro.com.backpack.model.entities.Item item);
|
||||
int getItemsCount();
|
||||
|
||||
|
||||
hikapro.com.backpack.model.entities.Item findItem(int id);
|
||||
hikapro.com.backpack.model.entities.Item getItemByPosition(int categoryId, int position);
|
||||
int getItemsCount(int categoryId);
|
||||
hikapro.com.backpack.model.entities.Item getItemByPosition(int position);
|
||||
|
||||
hikapro.com.backpack.model.entities.Category getCategoryByPosition(int position);
|
||||
int getCategoriesCount();
|
||||
void setPresenter(Presenter.ItemList presenter);
|
||||
|
|
|
@ -5,9 +5,9 @@ import android.os.Message;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import hikapro.com.backpack.model.database.Command;
|
||||
import hikapro.com.backpack.model.database.DAO;
|
||||
import hikapro.com.backpack.model.database.Event;
|
||||
import hikapro.com.backpack.model.dao.Command;
|
||||
import hikapro.com.backpack.model.dao.DAO;
|
||||
import hikapro.com.backpack.model.dao.Event;
|
||||
import hikapro.com.backpack.model.entities.Set;
|
||||
import hikapro.com.backpack.presenter.Presenter;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package hikapro.com.backpack.model.database;
|
||||
package hikapro.com.backpack.model.dao;
|
||||
|
||||
/**
|
||||
* Created by tariel on 27/04/16.
|
||||
|
@ -18,6 +18,7 @@ public interface Command {
|
|||
int ITEM_INSERT = 0x79;
|
||||
int ITEM_PACK = 0x7A;
|
||||
int ITEM_UNPACK = 0x7B;
|
||||
int ITEM_GET_CATEGORIES = 0x7C;
|
||||
|
||||
int MY_LIST_POST = 0x8C;
|
||||
int MY_LIST_ITEM_ADD = 0x8D;
|
|
@ -1,4 +1,4 @@
|
|||
package hikapro.com.backpack.model.database;
|
||||
package hikapro.com.backpack.model.dao;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
|
@ -132,6 +132,12 @@ public class DAO {
|
|||
threadPool.execute(setTask);
|
||||
break;
|
||||
|
||||
case Command.ITEM_GET_CATEGORIES :
|
||||
itemTask = new ItemTask(Command.ITEM_GET_CATEGORIES,
|
||||
Process.THREAD_PRIORITY_MORE_FAVORABLE);
|
||||
threadPool.execute(itemTask);
|
||||
break;
|
||||
|
||||
case Command.ITEM_INSERT :
|
||||
itemTask = new ItemTask(Command.ITEM_INSERT,
|
||||
Process.THREAD_PRIORITY_BACKGROUND);
|
||||
|
@ -590,6 +596,16 @@ public class DAO {
|
|||
else
|
||||
message.what = Event.ITEM_UNPACK_ERROR;
|
||||
break;
|
||||
|
||||
case Command.ITEM_GET_CATEGORIES :
|
||||
Hashtable<Integer, Category> res = readCategories();
|
||||
if (res.isEmpty())
|
||||
message.what = Event.ITEM_CATEGORY_LOAD_ERROR;
|
||||
else {
|
||||
message.what = Event.ITEM_CATEGORY_LOAD_COMPLETED;
|
||||
message.obj = res;
|
||||
}
|
||||
break;
|
||||
}
|
||||
handler.sendMessage(message);
|
||||
}
|
||||
|
@ -630,22 +646,8 @@ public class DAO {
|
|||
message.what = Event.SET_ITEMS_LOAD_ERROR;
|
||||
else {
|
||||
Collections.sort(items);
|
||||
Hashtable<Integer, Category> categories = readCategories();
|
||||
Hashtable<Category, List<Item>> result = new Hashtable<>(20, 0.9f);
|
||||
Category category;
|
||||
|
||||
for (Item item : items) {
|
||||
category = categories.get(item.getCategory());
|
||||
if (result.containsKey(category)) {
|
||||
result.get(category).add(item);
|
||||
} else {
|
||||
List<Item> innerList = new ArrayList<>(20);
|
||||
innerList.add(item);
|
||||
result.put(category, innerList);
|
||||
}
|
||||
}
|
||||
message.what = Event.SET_ITEMS_LOAD_COMPLETED;
|
||||
message.obj = result;
|
||||
message.obj = items;
|
||||
message.arg1 = setId;
|
||||
}
|
||||
break;
|
|
@ -1,8 +1,7 @@
|
|||
package hikapro.com.backpack.model.database;
|
||||
package hikapro.com.backpack.model.dao;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
|
@ -1,4 +1,4 @@
|
|||
package hikapro.com.backpack.model.database;
|
||||
package hikapro.com.backpack.model.dao;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
|
@ -1,4 +1,4 @@
|
|||
package hikapro.com.backpack.model.database;
|
||||
package hikapro.com.backpack.model.dao;
|
||||
|
||||
/**
|
||||
* Created by tariel on 27/04/16.
|
||||
|
@ -24,12 +24,14 @@ public interface Event {
|
|||
int ITEM_DELETE_ERROR = -0x16;
|
||||
int ITEM_PACK_ERROR = -0x17;
|
||||
int ITEM_UNPACK_ERROR = -0x18;
|
||||
int ITEM_CATEGORY_LOAD_ERROR = -0x19;
|
||||
|
||||
int ITEM_FROM_SET_DELETED = 0x14;
|
||||
int ITEM_INSERTED = 0x15;
|
||||
int ITEM_DELETED = 0x16;
|
||||
int ITEM_PACKED = 0x17;
|
||||
int ITEM_UNPACKED = 0x18;
|
||||
int ITEM_CATEGORY_LOAD_COMPLETED = 0x19;
|
||||
|
||||
int MY_LIST_POST_ERROR = -0x28;
|
||||
int MY_LIST_ITEM_ADD_ERROR = -0x29;
|
|
@ -1,4 +1,4 @@
|
|||
package hikapro.com.backpack.model.database;
|
||||
package hikapro.com.backpack.model.dao;
|
||||
|
||||
/**
|
||||
* Created by tariel on 27/04/16.
|
|
@ -2,27 +2,24 @@ package hikapro.com.backpack.presenter;
|
|||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.widget.DefaultItemAnimator;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import hikapro.com.backpack.R;
|
||||
import hikapro.com.backpack.model.ItemModel;
|
||||
import hikapro.com.backpack.model.Model;
|
||||
import hikapro.com.backpack.model.entities.Item;
|
||||
import hikapro.com.backpack.model.entities.Set;
|
||||
import hikapro.com.backpack.presenter.adapters.CategoryListAdapter;
|
||||
import hikapro.com.backpack.presenter.adapters.helper.items.DividerDecoration;
|
||||
import hikapro.com.backpack.presenter.adapters.ItemListAdapter;
|
||||
import hikapro.com.backpack.presenter.adapters.helper.items.ItemSwipeCallback;
|
||||
import hikapro.com.backpack.presenter.adapters.helper.items.StickyHeaderDecoration;
|
||||
import hikapro.com.backpack.view.View;
|
||||
import hikapro.com.backpack.view.recycler.CategoryViewHolder;
|
||||
import hikapro.com.backpack.view.recycler.ItemViewHolder;
|
||||
|
||||
/**
|
||||
* Created by tariel on 20/04/16.
|
||||
|
@ -34,10 +31,12 @@ public class ItemListPresenter implements Presenter.ItemList {
|
|||
private WeakReference<View.ItemList> view;
|
||||
private Model.Item model;
|
||||
private Set set;
|
||||
private CategoryListAdapter categoryListAdapter;
|
||||
private ItemListAdapter adapter;
|
||||
RecyclerView recycler;
|
||||
|
||||
public ItemListPresenter() {
|
||||
this.categoryListAdapter = new CategoryListAdapter(this);
|
||||
this.adapter = new ItemListAdapter(this);
|
||||
adapter.setHasStableIds(true);
|
||||
}
|
||||
|
||||
// life cycle -->
|
||||
|
@ -56,10 +55,25 @@ public class ItemListPresenter implements Presenter.ItemList {
|
|||
set = (Set) savedInstanceState.getSerializable(BUNDLE_SET_LIST_KEY);
|
||||
android.view.View view = inflater.inflate(R.layout.fragment_item_list, container, false);
|
||||
LinearLayoutManager llm = new LinearLayoutManager(getActivityContext());
|
||||
RecyclerView mainRecycler = (RecyclerView) view.findViewById(R.id.categories_main_recycler);
|
||||
mainRecycler.setLayoutManager(llm);
|
||||
mainRecycler.setAdapter(categoryListAdapter);
|
||||
mainRecycler.setItemAnimator(new DefaultItemAnimator());
|
||||
recycler = (RecyclerView) view.findViewById(R.id.items_recycler);
|
||||
recycler.setLayoutManager(llm);
|
||||
recycler.setAdapter(adapter);
|
||||
|
||||
final StickyHeaderDecoration decoration = new StickyHeaderDecoration(adapter);
|
||||
recycler.addItemDecoration(decoration);
|
||||
recycler.addItemDecoration(new DividerDecoration(getActivityContext()));
|
||||
adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
|
||||
@Override
|
||||
public void onChanged() {
|
||||
decoration.invalidateHeaders();
|
||||
}
|
||||
});
|
||||
ItemSwipeCallback itemSwipeCallback = new ItemSwipeCallback(0, ItemTouchHelper.LEFT, adapter, getActivityContext());
|
||||
|
||||
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(itemSwipeCallback);
|
||||
itemTouchHelper.attachToRecyclerView(recycler);
|
||||
adapter.setUndoOn(true);
|
||||
|
||||
model.executeQuery();
|
||||
return view;
|
||||
}
|
||||
|
@ -71,68 +85,11 @@ public class ItemListPresenter implements Presenter.ItemList {
|
|||
|
||||
// life cycle <--
|
||||
|
||||
// recycler -->
|
||||
|
||||
@Override
|
||||
public CategoryViewHolder createViewHolderCategory(ViewGroup parent, int viewType) {
|
||||
CategoryViewHolder viewHolder;
|
||||
android.view.View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.section,
|
||||
parent, false);
|
||||
viewHolder = new CategoryViewHolder(v);
|
||||
return viewHolder;
|
||||
}
|
||||
@Override
|
||||
public void bindViewHolderCategory(CategoryViewHolder holder, int position) {
|
||||
final hikapro.com.backpack.model.entities.Category category = model.getCategoryByPosition(position);
|
||||
holder.sectionText.setText(category.getName());
|
||||
LinearLayoutManager llm = new LinearLayoutManager(getActivityContext());
|
||||
holder.itemsRecycler.setLayoutManager(llm);
|
||||
ItemListAdapter itemListAdapter = new ItemListAdapter(this);
|
||||
itemListAdapter.setCategoryId(category.getId());
|
||||
holder.itemsRecycler.setAdapter(itemListAdapter);
|
||||
holder.itemsRecycler.setItemAnimator(new DefaultItemAnimator());
|
||||
categoryListAdapter.addItemAdapter(itemListAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemViewHolder createViewHolderItem(ViewGroup parent, int viewType) {
|
||||
ItemViewHolder viewHolder;
|
||||
android.view.View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item,
|
||||
parent, false);
|
||||
viewHolder = new ItemViewHolder(v);
|
||||
return viewHolder;
|
||||
}
|
||||
@Override
|
||||
public void bindViewHolderItem(final ItemViewHolder holder, final int position, int categoryId) {
|
||||
|
||||
hikapro.com.backpack.model.entities.Item item = model.getItemByPosition(categoryId, position);
|
||||
holder.title.setText(item.getName());
|
||||
holder.id = item.getId();
|
||||
holder.title.setOnClickListener(new android.view.View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(android.view.View view) {
|
||||
clickItem(holder.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
@Override
|
||||
public int getItemsCount(int categoryId) {
|
||||
return model.getItemsCount(categoryId);//TODO category Id
|
||||
}
|
||||
@Override
|
||||
public int getCategoriesCount() {
|
||||
return model.getCategoriesCount();
|
||||
}
|
||||
|
||||
|
||||
// recycler <--
|
||||
|
||||
// process -->
|
||||
|
||||
@Override
|
||||
public void notifyDataSetChanged() {
|
||||
categoryListAdapter.notifyDataSetChanged();
|
||||
categoryListAdapter.notifyItemAdapters();
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -156,6 +113,12 @@ public class ItemListPresenter implements Presenter.ItemList {
|
|||
public void setModel(Model.Item model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Model.Item getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context getAppContext() {
|
||||
try {
|
||||
|
@ -186,6 +149,11 @@ public class ItemListPresenter implements Presenter.ItemList {
|
|||
throw new NullPointerException("View is unavailable");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void filter(String query) {
|
||||
adapter.filter(query);
|
||||
recycler.scrollToPosition(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set getCurrentSet() {
|
||||
|
|
|
@ -3,15 +3,12 @@ package hikapro.com.backpack.presenter;
|
|||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.DragEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import hikapro.com.backpack.model.Model;
|
||||
import hikapro.com.backpack.model.entities.Item;
|
||||
import hikapro.com.backpack.model.entities.Set;
|
||||
import hikapro.com.backpack.view.recycler.CategoryViewHolder;
|
||||
import hikapro.com.backpack.view.recycler.DetailViewHolder;
|
||||
import hikapro.com.backpack.view.recycler.ItemViewHolder;
|
||||
import hikapro.com.backpack.view.recycler.SetViewHolder;
|
||||
|
@ -45,21 +42,18 @@ public interface Presenter {
|
|||
}
|
||||
|
||||
interface ItemList extends Base {
|
||||
ItemViewHolder createViewHolderItem(ViewGroup parent, int viewType);
|
||||
void bindViewHolderItem(ItemViewHolder holder, int position, int categoryId);
|
||||
int getItemsCount(int categoryId);
|
||||
CategoryViewHolder createViewHolderCategory(ViewGroup parent, int viewType);
|
||||
void bindViewHolderCategory(CategoryViewHolder holder, int position);
|
||||
int getCategoriesCount();
|
||||
|
||||
void onDestroy(boolean isChangingConfiguration);
|
||||
android.view.View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState);
|
||||
void setView(hikapro.com.backpack.view.View.ItemList view);
|
||||
void setModel(Model.Item model);
|
||||
Model.Item getModel();
|
||||
void notifyDataSetChanged();
|
||||
Set getCurrentSet();
|
||||
void showMessage(String message);
|
||||
void onSaveInstanceState(Bundle outState);
|
||||
void clickItem(int itemId);
|
||||
void filter(String query);
|
||||
}
|
||||
|
||||
interface ItemDetail extends Base {
|
||||
|
|
|
@ -17,8 +17,8 @@ import hikapro.com.backpack.R;
|
|||
import hikapro.com.backpack.model.Model;
|
||||
import hikapro.com.backpack.model.entities.Set;
|
||||
import hikapro.com.backpack.presenter.adapters.SetListAdapter;
|
||||
import hikapro.com.backpack.presenter.adapters.helper.OnStartDragListener;
|
||||
import hikapro.com.backpack.presenter.adapters.helper.SimpleItemTouchHelperCallback;
|
||||
import hikapro.com.backpack.presenter.adapters.helper.sets.OnStartDragListener;
|
||||
import hikapro.com.backpack.presenter.adapters.helper.sets.SimpleItemTouchHelperCallback;
|
||||
import hikapro.com.backpack.view.View;
|
||||
import hikapro.com.backpack.view.recycler.SetViewHolder;
|
||||
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
package hikapro.com.backpack.presenter.adapters;
|
||||
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import hikapro.com.backpack.presenter.Presenter;
|
||||
import hikapro.com.backpack.view.recycler.CategoryViewHolder;
|
||||
|
||||
/**
|
||||
* Created by tariel on 20/04/16.
|
||||
*/
|
||||
public class CategoryListAdapter extends RecyclerView.Adapter<CategoryViewHolder> {
|
||||
|
||||
private Presenter.ItemList presenter;
|
||||
private List<ItemListAdapter> itemAdapters;
|
||||
|
||||
public CategoryListAdapter(Presenter.ItemList presenter) {
|
||||
this.presenter = presenter;
|
||||
this.itemAdapters = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return presenter.getCategoriesCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(CategoryViewHolder holder, int position) {
|
||||
presenter.bindViewHolderCategory(holder, position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CategoryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
return presenter.createViewHolderCategory(parent, viewType);
|
||||
}
|
||||
|
||||
public void addItemAdapter(ItemListAdapter adapter) {
|
||||
itemAdapters.add(adapter);
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
public void notifyItemAdapters() {
|
||||
for (ItemListAdapter adapter : itemAdapters) {
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,18 +1,35 @@
|
|||
package hikapro.com.backpack.presenter.adapters;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.os.Handler;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import hikapro.com.backpack.R;
|
||||
import hikapro.com.backpack.model.Model;
|
||||
import hikapro.com.backpack.model.entities.Category;
|
||||
import hikapro.com.backpack.model.entities.Item;
|
||||
import hikapro.com.backpack.presenter.ItemListPresenter;
|
||||
import hikapro.com.backpack.presenter.Presenter;
|
||||
import hikapro.com.backpack.presenter.adapters.helper.items.StickyHeaderAdapter;
|
||||
import hikapro.com.backpack.view.recycler.HeaderViewHolder;
|
||||
import hikapro.com.backpack.view.recycler.ItemViewHolder;
|
||||
|
||||
/**
|
||||
* Created by tariel on 20/04/16.
|
||||
* Created by tariel on 01/05/16.
|
||||
*/
|
||||
public class ItemListAdapter extends RecyclerView.Adapter<ItemViewHolder> {
|
||||
public class ItemListAdapter extends RecyclerView.Adapter<ItemViewHolder> implements StickyHeaderAdapter<HeaderViewHolder> {
|
||||
|
||||
private static final int PENDING_REMOVAL_TIMEOUT = 4000; // 4sec
|
||||
|
||||
boolean undoOn; // is undo on, you can turn it on from the toolbar menu
|
||||
private Handler handler = new Handler(); // hanlder for running delayed runnables
|
||||
HashMap<Item, Runnable> pendingRunables = new HashMap<>(); // map of items to pending runnables, so we can cancel a removal if need be
|
||||
private Presenter.ItemList presenter;
|
||||
private int categoryId;
|
||||
|
||||
public ItemListAdapter(Presenter.ItemList presenter) {
|
||||
this.presenter = presenter;
|
||||
|
@ -20,19 +37,145 @@ public class ItemListAdapter extends RecyclerView.Adapter<ItemViewHolder> {
|
|||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return presenter.getItemsCount(categoryId);
|
||||
int res = presenter.getModel().getItemsCount();
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return presenter.getModel().getItemByPosition(position).getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ItemViewHolder holder, final int position) {
|
||||
|
||||
final Item item = presenter.getModel().getItemByPosition(position);
|
||||
if (presenter.getModel().isPendingRemoval(item)) {
|
||||
// we need to show the "undo" state of the row
|
||||
holder.itemView.setBackgroundColor(Color.RED);
|
||||
holder.title.setVisibility(View.GONE);
|
||||
holder.undoButton.setVisibility(View.VISIBLE);
|
||||
holder.undoButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// user wants to undo the removal, let's cancel the pending task
|
||||
Runnable pendingRemovalRunnable = pendingRunables.get(item);
|
||||
pendingRunables.remove(item);
|
||||
if (pendingRemovalRunnable != null) handler.removeCallbacks(pendingRemovalRunnable);
|
||||
presenter.getModel().pendingRemoveCancel(item);
|
||||
// this will rebind the row in "normal" state
|
||||
notifyItemChanged(position);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
holder.title.setVisibility(View.VISIBLE);
|
||||
holder.title.setText(item.getName());
|
||||
holder.id = item.getId();
|
||||
holder.categoryId = item.getCategory();
|
||||
holder.itemView.setBackgroundColor(0x33FF99);
|
||||
holder.undoButton.setVisibility(View.GONE);
|
||||
holder.undoButton.setOnClickListener(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
return presenter.createViewHolderItem(parent, viewType);
|
||||
ItemViewHolder viewHolder;
|
||||
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_item,
|
||||
parent, false);
|
||||
viewHolder = new ItemViewHolder(v);
|
||||
return viewHolder;
|
||||
}
|
||||
|
||||
public void setUndoOn(boolean undoOn) {
|
||||
this.undoOn = undoOn;
|
||||
}
|
||||
|
||||
public boolean isUndoOn() {
|
||||
return undoOn;
|
||||
}
|
||||
|
||||
public void add(Item item) {
|
||||
presenter.getModel().insertItem(item);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
|
||||
public void clear() {
|
||||
presenter.getModel().clear();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void filter(String query) {
|
||||
presenter.getModel().filter(query);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ItemViewHolder holder, int position) {
|
||||
presenter.bindViewHolderItem(holder, position, categoryId);
|
||||
public void setHasStableIds(boolean hasStableIds) {
|
||||
super.setHasStableIds(hasStableIds);
|
||||
}
|
||||
public void setCategoryId(int categoryId) {
|
||||
this.categoryId = categoryId;
|
||||
|
||||
@Override
|
||||
public long getHeaderId(int position) {
|
||||
/*if (position == 0) {
|
||||
return -1;
|
||||
} else {*/
|
||||
return presenter.getModel().getHeaderId(position);
|
||||
//}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent) {
|
||||
HeaderViewHolder viewHolder;
|
||||
View v = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.view_header, parent, false);
|
||||
viewHolder = new HeaderViewHolder(v);
|
||||
return viewHolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindHeaderViewHolder(HeaderViewHolder holder, int position) {
|
||||
Category category = presenter.getModel().getCategoryByPosition(position);
|
||||
holder.id = category.getId();
|
||||
holder.title.setText(category.getName());
|
||||
holder.title.setBackgroundColor(0x2B1E15);
|
||||
}
|
||||
|
||||
public void pendingRemoval(final int position) {
|
||||
final Item item = presenter.getModel().getItemByPosition(position);
|
||||
|
||||
if (! presenter.getModel().isPendingRemoval(item)) {
|
||||
presenter.getModel().pendingRemove(item);
|
||||
// this will redraw row in "undo" state
|
||||
notifyItemChanged(position);
|
||||
// let's create, store and post a runnable to remove the item
|
||||
Runnable pendingRemovalRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
remove(item, position);
|
||||
}
|
||||
};
|
||||
handler.postDelayed(pendingRemovalRunnable, PENDING_REMOVAL_TIMEOUT);
|
||||
pendingRunables.put(item, pendingRemovalRunnable);
|
||||
}
|
||||
}
|
||||
|
||||
public void remove(Item item, int position) {
|
||||
presenter.getModel().deleteItem(item);
|
||||
notifyItemRemoved(position);
|
||||
}
|
||||
|
||||
public void remove(int position) {
|
||||
Item item = presenter.getModel().getItemByPosition(position);
|
||||
presenter.getModel().deleteItem(item);
|
||||
notifyItemRemoved(position);
|
||||
}
|
||||
|
||||
public boolean isPendingRemoval(int position) {
|
||||
Item item = presenter.getModel().getItemByPosition(position);
|
||||
return presenter.getModel().isPendingRemoval(item);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import android.support.v7.widget.RecyclerView;
|
|||
import android.view.ViewGroup;
|
||||
|
||||
import hikapro.com.backpack.presenter.SetListPresenter;
|
||||
import hikapro.com.backpack.presenter.adapters.helper.ItemTouchHelperAdapter;
|
||||
import hikapro.com.backpack.presenter.adapters.helper.sets.ItemTouchHelperAdapter;
|
||||
import hikapro.com.backpack.view.recycler.SetViewHolder;
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
package hikapro.com.backpack.presenter.adapters.helper.items;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
/**
|
||||
* Created by tariel on 30/04/16.
|
||||
*/
|
||||
public class DimensionCalculator {
|
||||
|
||||
/**
|
||||
* Populates {@link Rect} with margins for any view.
|
||||
*
|
||||
*
|
||||
* @param margins rect to populate
|
||||
* @param view for which to get margins
|
||||
*/
|
||||
public void initMargins(Rect margins, View view) {
|
||||
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
|
||||
|
||||
if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
|
||||
ViewGroup.MarginLayoutParams marginLayoutParams = (ViewGroup.MarginLayoutParams) layoutParams;
|
||||
initMarginRect(margins, marginLayoutParams);
|
||||
} else {
|
||||
margins.set(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts {@link ViewGroup.MarginLayoutParams} into a representative {@link Rect}.
|
||||
*
|
||||
* @param marginRect Rect to be initialized with margins coordinates, where
|
||||
* {@link ViewGroup.MarginLayoutParams#leftMargin} is equivalent to {@link Rect#left}, etc.
|
||||
* @param marginLayoutParams margins to populate the Rect with
|
||||
*/
|
||||
private void initMarginRect(Rect marginRect, ViewGroup.MarginLayoutParams marginLayoutParams) {
|
||||
marginRect.set(
|
||||
marginLayoutParams.leftMargin,
|
||||
marginLayoutParams.topMargin,
|
||||
marginLayoutParams.rightMargin,
|
||||
marginLayoutParams.bottomMargin
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
package hikapro.com.backpack.presenter.adapters.helper.items;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
|
||||
|
||||
public class DividerDecoration extends RecyclerView.ItemDecoration {
|
||||
|
||||
private static final int[] ATTRS = new int[]{
|
||||
android.R.attr.listDivider
|
||||
};
|
||||
|
||||
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
|
||||
|
||||
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
|
||||
|
||||
private Drawable mDivider;
|
||||
|
||||
public DividerDecoration(Context context) {
|
||||
final TypedArray a = context.obtainStyledAttributes(ATTRS);
|
||||
mDivider = a.getDrawable(0);
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
private int getOrientation(RecyclerView parent) {
|
||||
LinearLayoutManager layoutManager;
|
||||
try {
|
||||
layoutManager = (LinearLayoutManager) parent.getLayoutManager();
|
||||
} catch (ClassCastException e) {
|
||||
throw new IllegalStateException("DividerDecoration can only be used with a " +
|
||||
"LinearLayoutManager.", e);
|
||||
}
|
||||
return layoutManager.getOrientation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
|
||||
super.onDraw(c, parent, state);
|
||||
|
||||
if (getOrientation(parent) == VERTICAL_LIST) {
|
||||
drawVertical(c, parent);
|
||||
} else {
|
||||
drawHorizontal(c, parent);
|
||||
}
|
||||
}
|
||||
|
||||
public void drawVertical(Canvas c, RecyclerView parent) {
|
||||
final int left = parent.getPaddingLeft();
|
||||
final int right = parent.getWidth() - parent.getPaddingRight();
|
||||
final int recyclerViewTop = parent.getPaddingTop();
|
||||
final int recyclerViewBottom = parent.getHeight() - parent.getPaddingBottom();
|
||||
final int childCount = parent.getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
final View child = parent.getChildAt(i);
|
||||
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
|
||||
.getLayoutParams();
|
||||
final int top = Math.max(recyclerViewTop, child.getBottom() + params.bottomMargin);
|
||||
final int bottom = Math.min(recyclerViewBottom, top + mDivider.getIntrinsicHeight());
|
||||
mDivider.setBounds(left, top, right, bottom);
|
||||
mDivider.draw(c);
|
||||
}
|
||||
}
|
||||
|
||||
public void drawHorizontal(Canvas c, RecyclerView parent) {
|
||||
final int top = parent.getPaddingTop();
|
||||
final int bottom = parent.getHeight() - parent.getPaddingBottom();
|
||||
final int recyclerViewLeft = parent.getPaddingLeft();
|
||||
final int recyclerViewRight = parent.getWidth() - parent.getPaddingRight();
|
||||
final int childCount = parent.getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
final View child = parent.getChildAt(i);
|
||||
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
|
||||
.getLayoutParams();
|
||||
final int left = Math.max(recyclerViewLeft, child.getRight() + params.rightMargin);
|
||||
final int right = Math.min(recyclerViewRight, left + mDivider.getIntrinsicHeight());
|
||||
mDivider.setBounds(left, top, right, bottom);
|
||||
mDivider.draw(c);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
|
||||
super.getItemOffsets(outRect, view, parent, state);
|
||||
if (getOrientation(parent) == VERTICAL_LIST) {
|
||||
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
|
||||
} else {
|
||||
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,256 @@
|
|||
package hikapro.com.backpack.presenter.adapters.helper.items;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
/**
|
||||
* Created by tariel on 30/04/16.
|
||||
*/
|
||||
public class HeaderPositionCalculator {
|
||||
|
||||
private final StickyHeaderAdapter adapter;
|
||||
private final HeaderProvider headerProvider;
|
||||
private final DimensionCalculator dimensionCalculator;
|
||||
|
||||
/**
|
||||
* The following fields are used as buffers for internal calculations. Their sole purpose is to avoid
|
||||
* allocating new Rect every time we need one.
|
||||
*/
|
||||
private final Rect tempRect1 = new Rect();
|
||||
private final Rect tempRect2 = new Rect();
|
||||
|
||||
public HeaderPositionCalculator(StickyHeaderAdapter adapter, HeaderProvider headerProvider,
|
||||
DimensionCalculator dimensionCalculator) {
|
||||
this.adapter = adapter;
|
||||
this.headerProvider = headerProvider;
|
||||
this.dimensionCalculator = dimensionCalculator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a view should have a sticky header.
|
||||
* The view has a sticky header if:
|
||||
* 1. It is the first element in the recycler view
|
||||
* 2. It has a valid ID associated to its position
|
||||
*
|
||||
* @param itemView given by the RecyclerView
|
||||
* @param orientation of the Recyclerview
|
||||
* @param position of the list item in question
|
||||
* @return True if the view should have a sticky header
|
||||
*/
|
||||
public boolean hasStickyHeader(View itemView, int orientation, int position) {
|
||||
int offset, margin;
|
||||
dimensionCalculator.initMargins(tempRect1, itemView);
|
||||
if (orientation == LinearLayout.VERTICAL) {
|
||||
offset = itemView.getTop();
|
||||
margin = tempRect1.top;
|
||||
} else {
|
||||
offset = itemView.getLeft();
|
||||
margin = tempRect1.left;
|
||||
}
|
||||
boolean ret = offset <= margin;
|
||||
ret = ret && adapter.getHeaderId(position) >= 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if an item in the list should have a header that is different than the item in the
|
||||
* list that immediately precedes it. Items with no headers will always return false.
|
||||
*
|
||||
* @param position of the list item in questions
|
||||
* @param isReverseLayout TRUE if layout manager has flag isReverseLayout
|
||||
* @return true if this item has a different header than the previous item in the list
|
||||
*/
|
||||
public boolean hasNewHeader(int position) {
|
||||
if (indexOutOfBounds(position)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long headerId = adapter.getHeaderId(position);
|
||||
|
||||
if (headerId < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long nextItemHeaderId = -1;//TODO -1
|
||||
int nextItemPosition = position - 1; // TODO -1
|
||||
if (!indexOutOfBounds(nextItemPosition)) {
|
||||
nextItemHeaderId = adapter.getHeaderId(nextItemPosition);
|
||||
}
|
||||
int firstItemPosition = 0;
|
||||
|
||||
return position == firstItemPosition || headerId != nextItemHeaderId;
|
||||
}
|
||||
|
||||
private boolean indexOutOfBounds(int position) {
|
||||
return position < 0 || position >= adapter.getItemCount();
|
||||
}
|
||||
|
||||
public void initHeaderBounds(Rect bounds, RecyclerView recyclerView, View header, View firstView, boolean firstHeader) {
|
||||
initDefaultHeaderOffset(bounds, recyclerView, header, firstView, LinearLayout.VERTICAL);
|
||||
|
||||
if (firstHeader && isStickyHeaderBeingPushedOffscreen(recyclerView, header)) {
|
||||
View viewAfterNextHeader = getFirstViewUnobscuredByHeader(recyclerView, header);
|
||||
int firstViewUnderHeaderPosition = recyclerView.getChildAdapterPosition(viewAfterNextHeader);
|
||||
View secondHeader = headerProvider.getHeader(recyclerView, firstViewUnderHeaderPosition);
|
||||
translateHeaderWithNextHeader(recyclerView, LinearLayout.VERTICAL, bounds,
|
||||
header, viewAfterNextHeader, secondHeader);
|
||||
}
|
||||
}
|
||||
|
||||
private void initDefaultHeaderOffset(Rect headerMargins, RecyclerView recyclerView, View header, View firstView, int orientation) {
|
||||
int translationX, translationY;
|
||||
dimensionCalculator.initMargins(tempRect1, header);
|
||||
|
||||
ViewGroup.LayoutParams layoutParams = firstView.getLayoutParams();
|
||||
int leftMargin = 0;
|
||||
int topMargin = 0;
|
||||
if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
|
||||
ViewGroup.MarginLayoutParams marginLayoutParams = (ViewGroup.MarginLayoutParams) layoutParams;
|
||||
leftMargin = marginLayoutParams.leftMargin;
|
||||
topMargin = marginLayoutParams.topMargin;
|
||||
}
|
||||
|
||||
if (orientation == LinearLayoutManager.VERTICAL) {
|
||||
translationX = firstView.getLeft() - leftMargin + tempRect1.left;
|
||||
translationY = Math.max(
|
||||
firstView.getTop() - topMargin - header.getHeight() - tempRect1.bottom,
|
||||
getListTop(recyclerView) + tempRect1.top);
|
||||
} else {
|
||||
translationY = firstView.getTop() - topMargin + tempRect1.top;
|
||||
translationX = Math.max(
|
||||
firstView.getLeft() - leftMargin - header.getWidth() - tempRect1.right,
|
||||
getListLeft(recyclerView) + tempRect1.left);
|
||||
}
|
||||
|
||||
headerMargins.set(translationX, translationY, translationX + header.getWidth(),
|
||||
translationY + header.getHeight());
|
||||
}
|
||||
|
||||
private boolean isStickyHeaderBeingPushedOffscreen(RecyclerView recyclerView, View stickyHeader) {
|
||||
View viewAfterHeader = getFirstViewUnobscuredByHeader(recyclerView, stickyHeader);
|
||||
int firstViewUnderHeaderPosition = recyclerView.getChildAdapterPosition(viewAfterHeader);
|
||||
if (firstViewUnderHeaderPosition == RecyclerView.NO_POSITION) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int orientation = LinearLayoutManager.VERTICAL;
|
||||
|
||||
if (firstViewUnderHeaderPosition > 0 && hasNewHeader(firstViewUnderHeaderPosition)) {
|
||||
View nextHeader = headerProvider.getHeader(recyclerView, firstViewUnderHeaderPosition);
|
||||
dimensionCalculator.initMargins(tempRect1, nextHeader);
|
||||
dimensionCalculator.initMargins(tempRect2, stickyHeader);
|
||||
|
||||
if (orientation == LinearLayoutManager.VERTICAL) {
|
||||
int topOfNextHeader = viewAfterHeader.getTop() - tempRect1.bottom - nextHeader.getHeight() - tempRect1.top;
|
||||
int bottomOfThisHeader = recyclerView.getPaddingTop() + stickyHeader.getBottom() + tempRect2.top + tempRect2.bottom;
|
||||
if (topOfNextHeader < bottomOfThisHeader) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
int leftOfNextHeader = viewAfterHeader.getLeft() - tempRect1.right - nextHeader.getWidth() - tempRect1.left;
|
||||
int rightOfThisHeader = recyclerView.getPaddingLeft() + stickyHeader.getRight() + tempRect2.left + tempRect2.right;
|
||||
if (leftOfNextHeader < rightOfThisHeader) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void translateHeaderWithNextHeader(RecyclerView recyclerView, int orientation, Rect translation,
|
||||
View currentHeader, View viewAfterNextHeader, View nextHeader) {
|
||||
dimensionCalculator.initMargins(tempRect1, nextHeader);
|
||||
dimensionCalculator.initMargins(tempRect2, currentHeader);
|
||||
if (orientation == LinearLayoutManager.VERTICAL) {
|
||||
int topOfStickyHeader = getListTop(recyclerView) + tempRect2.top + tempRect2.bottom;
|
||||
int shiftFromNextHeader = viewAfterNextHeader.getTop() - nextHeader.getHeight() - tempRect1.bottom - tempRect1.top - currentHeader.getHeight() - topOfStickyHeader;
|
||||
if (shiftFromNextHeader < topOfStickyHeader) {
|
||||
translation.top += shiftFromNextHeader;
|
||||
}
|
||||
} else {
|
||||
int leftOfStickyHeader = getListLeft(recyclerView) + tempRect2.left + tempRect2.right;
|
||||
int shiftFromNextHeader = viewAfterNextHeader.getLeft() - nextHeader.getWidth() - tempRect1.right - tempRect1.left - currentHeader.getWidth() - leftOfStickyHeader;
|
||||
if (shiftFromNextHeader < leftOfStickyHeader) {
|
||||
translation.left += shiftFromNextHeader;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first item currently in the RecyclerView that is not obscured by a header.
|
||||
*
|
||||
* @param parent Recyclerview containing all the list items
|
||||
* @return first item that is fully beneath a header
|
||||
*/
|
||||
private View getFirstViewUnobscuredByHeader(RecyclerView parent, View firstHeader) {
|
||||
int step = 1;
|
||||
int from = 0;
|
||||
for (int i = from; i >= 0 && i <= parent.getChildCount() - 1; i += step) {
|
||||
View child = parent.getChildAt(i);
|
||||
if (!itemIsObscuredByHeader(parent, child, firstHeader, LinearLayout.VERTICAL)) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if an item is obscured by a header
|
||||
*
|
||||
*
|
||||
* @param parent
|
||||
* @param item to determine if obscured by header
|
||||
* @param header that might be obscuring the item
|
||||
* @param orientation of the {@link RecyclerView}
|
||||
* @return true if the item view is obscured by the header view
|
||||
*/
|
||||
private boolean itemIsObscuredByHeader(RecyclerView parent, View item, View header, int orientation) {
|
||||
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) item.getLayoutParams();
|
||||
dimensionCalculator.initMargins(tempRect1, header);
|
||||
|
||||
int adapterPosition = parent.getChildAdapterPosition(item);
|
||||
if (adapterPosition == RecyclerView.NO_POSITION || headerProvider.getHeader(parent, adapterPosition) != header) {
|
||||
// Resolves https://github.com/timehop/sticky-headers-recyclerview/issues/36
|
||||
// Handles an edge case where a trailing header is smaller than the current sticky header.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (orientation == LinearLayoutManager.VERTICAL) {
|
||||
int itemTop = item.getTop() - layoutParams.topMargin;
|
||||
int headerBottom = getListTop(parent) + header.getBottom() + tempRect1.bottom + tempRect1.top;
|
||||
if (itemTop >= headerBottom) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
int itemLeft = item.getLeft() - layoutParams.leftMargin;
|
||||
int headerRight = getListLeft(parent) + header.getRight() + tempRect1.right + tempRect1.left;
|
||||
if (itemLeft >= headerRight) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private int getListTop(RecyclerView view) {
|
||||
if (view.getLayoutManager().getClipToPadding()) {
|
||||
return view.getPaddingTop();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private int getListLeft(RecyclerView view) {
|
||||
if (view.getLayoutManager().getClipToPadding()) {
|
||||
return view.getPaddingLeft();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package hikapro.com.backpack.presenter.adapters.helper.items;
|
||||
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* Created by tariel on 30/04/16.
|
||||
*/
|
||||
public interface HeaderProvider {
|
||||
|
||||
/**
|
||||
* Will provide a header view for a given position in the RecyclerView
|
||||
*
|
||||
* @param recyclerView that will display the header
|
||||
* @param position that will be headed by the header
|
||||
* @return a header view for the given position and list
|
||||
*/
|
||||
View getHeader(RecyclerView recyclerView, int position);
|
||||
|
||||
/**
|
||||
* TODO: describe this functionality and its necessity
|
||||
*/
|
||||
void invalidate();
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package hikapro.com.backpack.presenter.adapters.helper.items;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* Created by tariel on 30/04/16.
|
||||
*/
|
||||
public class HeaderRenderer {
|
||||
|
||||
private final DimensionCalculator dimensionCalculator;
|
||||
|
||||
/**
|
||||
* The following field is used as a buffer for internal calculations. Its sole purpose is to avoid
|
||||
* allocating new Rect every time we need one.
|
||||
*/
|
||||
private final Rect mTempRect = new Rect();
|
||||
|
||||
public HeaderRenderer() {
|
||||
this.dimensionCalculator = new DimensionCalculator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a header to a canvas, offsetting by some x and y amount
|
||||
*
|
||||
* @param recyclerView the parent recycler view for drawing the header into
|
||||
* @param canvas the canvas on which to draw the header
|
||||
* @param header the view to draw as the header
|
||||
* @param offset a Rect used to define the x/y offset of the header. Specify x/y offset by setting
|
||||
* the {@link Rect#left} and {@link Rect#top} properties, respectively.
|
||||
*/
|
||||
public void drawHeader(RecyclerView recyclerView, Canvas canvas, View header, Rect offset) {
|
||||
canvas.save();
|
||||
|
||||
if (recyclerView.getLayoutManager().getClipToPadding()) {
|
||||
// Clip drawing of headers to the padding of the RecyclerView. Avoids drawing in the padding
|
||||
initClipRectForHeader(mTempRect, recyclerView, header);
|
||||
canvas.clipRect(mTempRect);
|
||||
}
|
||||
|
||||
canvas.translate(offset.left, offset.top);
|
||||
|
||||
header.draw(canvas);
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a clipping rect for the header based on the margins of the header and the padding of the
|
||||
* recycler.
|
||||
* FIXME: Currently right margin in VERTICAL orientation and bottom margin in HORIZONTAL
|
||||
* orientation are clipped so they look accurate, but the headers are not being drawn at the
|
||||
* correctly smaller width and height respectively.
|
||||
*
|
||||
* @param clipRect {@link Rect} for clipping a provided header to the padding of a recycler view
|
||||
* @param recyclerView for which to provide a header
|
||||
* @param header for clipping
|
||||
*/
|
||||
private void initClipRectForHeader(Rect clipRect, RecyclerView recyclerView, View header) {
|
||||
dimensionCalculator.initMargins(clipRect, header);
|
||||
|
||||
clipRect.set(
|
||||
recyclerView.getPaddingLeft(),
|
||||
recyclerView.getPaddingTop(),
|
||||
recyclerView.getWidth() - recyclerView.getPaddingRight() - clipRect.right,
|
||||
recyclerView.getHeight() - recyclerView.getPaddingBottom());
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package hikapro.com.backpack.presenter.adapters.helper.items;
|
||||
|
||||
import android.support.v4.util.LongSparseArray;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
/**
|
||||
* Created by tariel on 30/04/16.
|
||||
*/
|
||||
public class HeaderViewCache implements HeaderProvider {
|
||||
|
||||
private final StickyHeaderAdapter adapter;
|
||||
private final LongSparseArray<View> headerViews = new LongSparseArray<>();
|
||||
|
||||
public HeaderViewCache(StickyHeaderAdapter adapter) {
|
||||
this.adapter = adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getHeader(RecyclerView parent, int position) {
|
||||
long headerId = adapter.getHeaderId(position);
|
||||
|
||||
View header = headerViews.get(headerId);
|
||||
if (header == null) {
|
||||
//TODO - recycle views
|
||||
RecyclerView.ViewHolder viewHolder = adapter.onCreateHeaderViewHolder(parent);
|
||||
adapter.onBindHeaderViewHolder(viewHolder, position);
|
||||
header = viewHolder.itemView;
|
||||
if (header.getLayoutParams() == null) {
|
||||
header.setLayoutParams(new ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
}
|
||||
|
||||
int widthSpec;
|
||||
int heightSpec;
|
||||
|
||||
widthSpec = View.MeasureSpec.makeMeasureSpec(parent.getWidth(), View.MeasureSpec.EXACTLY);
|
||||
heightSpec = View.MeasureSpec.makeMeasureSpec(parent.getHeight(), View.MeasureSpec.UNSPECIFIED);
|
||||
|
||||
int childWidth = ViewGroup.getChildMeasureSpec(widthSpec,
|
||||
parent.getPaddingLeft() + parent.getPaddingRight(), header.getLayoutParams().width);
|
||||
int childHeight = ViewGroup.getChildMeasureSpec(heightSpec,
|
||||
parent.getPaddingTop() + parent.getPaddingBottom(), header.getLayoutParams().height);
|
||||
header.measure(childWidth, childHeight);
|
||||
header.layout(0, 0, header.getMeasuredWidth(), header.getMeasuredHeight());
|
||||
headerViews.put(headerId, header);
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate() {
|
||||
headerViews.clear();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
package hikapro.com.backpack.presenter.adapters.helper.items;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||
import android.view.View;
|
||||
|
||||
import hikapro.com.backpack.R;
|
||||
import hikapro.com.backpack.presenter.adapters.ItemListAdapter;
|
||||
|
||||
/**
|
||||
* Created by tariel on 02/05/16.
|
||||
*/
|
||||
public class ItemSwipeCallback extends ItemTouchHelper.SimpleCallback {
|
||||
|
||||
// we want to cache these and not allocate anything repeatedly in the onChildDraw method
|
||||
Drawable background;
|
||||
Drawable xMark;
|
||||
int xMarkMargin;
|
||||
boolean initiated;
|
||||
ItemListAdapter adapter;
|
||||
Context context;
|
||||
|
||||
public ItemSwipeCallback(int dragDirs, int swipeDirs, ItemListAdapter adapter, Context context) {
|
||||
super(dragDirs, swipeDirs);
|
||||
this.adapter = adapter;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
private void init() {
|
||||
background = new ColorDrawable(Color.RED);
|
||||
xMark = ContextCompat.getDrawable(context, R.drawable.ic_clear_24dp);
|
||||
xMark.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_ATOP);
|
||||
xMarkMargin = (int) context.getResources().getDimension(R.dimen.ic_clear_margin);
|
||||
initiated = true;
|
||||
}
|
||||
|
||||
// not important, we don't want drag & drop
|
||||
@Override
|
||||
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
|
||||
int position = viewHolder.getAdapterPosition();
|
||||
if (adapter.isUndoOn() && adapter.isPendingRemoval(position)) {
|
||||
return 0;
|
||||
}
|
||||
return super.getSwipeDirs(recyclerView, viewHolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
|
||||
int swipedPosition = viewHolder.getAdapterPosition();
|
||||
|
||||
boolean undoOn = adapter.isUndoOn();
|
||||
if (undoOn) {
|
||||
adapter.pendingRemoval(swipedPosition);
|
||||
} else {
|
||||
adapter.remove(swipedPosition);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
|
||||
View itemView = viewHolder.itemView;
|
||||
|
||||
// not sure why, but this method get's called for viewholder that are already swiped away
|
||||
if (viewHolder.getAdapterPosition() == -1) {
|
||||
// not interested in those
|
||||
return;
|
||||
}
|
||||
|
||||
if (!initiated) {
|
||||
init();
|
||||
}
|
||||
|
||||
// draw red background
|
||||
background.setBounds(itemView.getRight() + (int) dX, itemView.getTop(), itemView.getRight(), itemView.getBottom());
|
||||
background.draw(c);
|
||||
|
||||
// draw x mark
|
||||
int itemHeight = itemView.getBottom() - itemView.getTop();
|
||||
int intrinsicWidth = xMark.getIntrinsicWidth();
|
||||
int intrinsicHeight = xMark.getIntrinsicWidth();
|
||||
|
||||
int xMarkLeft = itemView.getRight() - xMarkMargin - intrinsicWidth;
|
||||
int xMarkRight = itemView.getRight() - xMarkMargin;
|
||||
int xMarkTop = itemView.getTop() + (itemHeight - intrinsicHeight)/2;
|
||||
int xMarkBottom = xMarkTop + intrinsicHeight;
|
||||
xMark.setBounds(xMarkLeft, xMarkTop, xMarkRight, xMarkBottom);
|
||||
|
||||
xMark.draw(c);
|
||||
|
||||
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package hikapro.com.backpack.presenter.adapters.helper.items;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* Created by tariel on 02/05/16.
|
||||
*/
|
||||
public class ItemSwipeDecoration extends RecyclerView.ItemDecoration {
|
||||
|
||||
// we want to cache this and not allocate anything repeatedly in the onDraw method
|
||||
Drawable background;
|
||||
boolean initiated;
|
||||
|
||||
private void init() {
|
||||
background = new ColorDrawable(Color.RED);
|
||||
initiated = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
|
||||
|
||||
if (!initiated) {
|
||||
init();
|
||||
}
|
||||
|
||||
// only if animation is in progress
|
||||
if (parent.getItemAnimator().isRunning()) {
|
||||
|
||||
// some items might be animating down and some items might be animating up to close the gap left by the removed item
|
||||
// this is not exclusive, both movement can be happening at the same time
|
||||
// to reproduce this leave just enough items so the first one and the last one would be just a little off screen
|
||||
// then remove one from the middle
|
||||
|
||||
// find first child with translationY > 0
|
||||
// and last one with translationY < 0
|
||||
// we're after a rect that is not covered in recycler-view views at this point in time
|
||||
View lastViewComingDown = null;
|
||||
View firstViewComingUp = null;
|
||||
|
||||
// this is fixed
|
||||
int left = 0;
|
||||
int right = parent.getWidth();
|
||||
|
||||
// this we need to find out
|
||||
int top = 0;
|
||||
int bottom = 0;
|
||||
|
||||
// find relevant translating views
|
||||
int childCount = parent.getLayoutManager().getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
View child = parent.getLayoutManager().getChildAt(i);
|
||||
if (child.getTranslationY() < 0) {
|
||||
// view is coming down
|
||||
lastViewComingDown = child;
|
||||
} else if (child.getTranslationY() > 0) {
|
||||
// view is coming up
|
||||
if (firstViewComingUp == null) {
|
||||
firstViewComingUp = child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lastViewComingDown != null && firstViewComingUp != null) {
|
||||
// views are coming down AND going up to fill the void
|
||||
top = lastViewComingDown.getBottom() + (int) lastViewComingDown.getTranslationY();
|
||||
bottom = firstViewComingUp.getTop() + (int) firstViewComingUp.getTranslationY();
|
||||
} else if (lastViewComingDown != null) {
|
||||
// views are going down to fill the void
|
||||
top = lastViewComingDown.getBottom() + (int) lastViewComingDown.getTranslationY();
|
||||
bottom = lastViewComingDown.getBottom();
|
||||
} else if (firstViewComingUp != null) {
|
||||
// views are coming up to fill the void
|
||||
top = firstViewComingUp.getTop();
|
||||
bottom = firstViewComingUp.getTop() + (int) firstViewComingUp.getTranslationY();
|
||||
}
|
||||
|
||||
background.setBounds(left, top, right, bottom);
|
||||
background.draw(c);
|
||||
|
||||
}
|
||||
super.onDraw(c, parent, state);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package hikapro.com.backpack.presenter.adapters.helper.items;
|
||||
|
||||
/**
|
||||
* Created by tariel on 30/04/16.
|
||||
*/
|
||||
public interface ItemVisibilityAdapter {
|
||||
|
||||
/**
|
||||
*
|
||||
* Return true the specified adapter position is visible, false otherwise
|
||||
*
|
||||
* The implementation of this method will typically return true if
|
||||
* the position is between the layout manager's findFirstVisibleItemPosition
|
||||
* and findLastVisibleItemPosition (inclusive).
|
||||
*
|
||||
* @param position the adapter position
|
||||
*/
|
||||
boolean isPositionVisible(final int position);
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package hikapro.com.backpack.presenter.adapters.helper.items;
|
||||
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
/**
|
||||
* Created by tariel on 01/05/16.
|
||||
*/
|
||||
public interface StickyHeaderAdapter<VH extends RecyclerView.ViewHolder> {
|
||||
|
||||
long getHeaderId(int position);
|
||||
VH onCreateHeaderViewHolder(ViewGroup parent);
|
||||
void onBindHeaderViewHolder(VH holder, int position);
|
||||
int getItemCount();
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
package hikapro.com.backpack.presenter.adapters.helper.items;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.SparseArray;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
/**
|
||||
* Created by tariel on 01/05/16.
|
||||
*/
|
||||
public class StickyHeaderDecoration extends RecyclerView.ItemDecoration {
|
||||
|
||||
private final StickyHeaderAdapter adapter;
|
||||
private final ItemVisibilityAdapter visibilityAdapter;
|
||||
private final SparseArray<Rect> headerRects = new SparseArray<>();
|
||||
private final HeaderProvider headerProvider;
|
||||
private final HeaderPositionCalculator headerPositionCalculator;
|
||||
private final HeaderRenderer renderer;
|
||||
private final DimensionCalculator dimensionCalculator;
|
||||
|
||||
private final Rect rect = new Rect();
|
||||
|
||||
|
||||
public StickyHeaderDecoration(StickyHeaderAdapter adapter) {
|
||||
this(adapter, new DimensionCalculator(), null);
|
||||
}
|
||||
|
||||
private StickyHeaderDecoration(StickyHeaderAdapter adapter,
|
||||
DimensionCalculator dimensionCalculator, ItemVisibilityAdapter visibilityAdapter) {
|
||||
this(adapter, dimensionCalculator, new HeaderRenderer(), new HeaderViewCache(adapter), visibilityAdapter);
|
||||
}
|
||||
|
||||
private StickyHeaderDecoration(StickyHeaderAdapter adapter, DimensionCalculator dimensionCalculator,
|
||||
HeaderRenderer headerRenderer, HeaderProvider headerProvider,
|
||||
ItemVisibilityAdapter visibilityAdapter) {
|
||||
this(adapter, headerRenderer, dimensionCalculator, headerProvider,
|
||||
new HeaderPositionCalculator(adapter, headerProvider,
|
||||
dimensionCalculator), visibilityAdapter);
|
||||
}
|
||||
|
||||
private StickyHeaderDecoration(StickyHeaderAdapter adapter, HeaderRenderer headerRenderer,
|
||||
DimensionCalculator dimensionCalculator, HeaderProvider headerProvider,
|
||||
HeaderPositionCalculator headerPositionCalculator, ItemVisibilityAdapter visibilityAdapter) {
|
||||
this.adapter = adapter;
|
||||
this.headerProvider = headerProvider;
|
||||
this.renderer = headerRenderer;
|
||||
this.dimensionCalculator = dimensionCalculator;
|
||||
this.headerPositionCalculator = headerPositionCalculator;
|
||||
this.visibilityAdapter = visibilityAdapter;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
|
||||
super.getItemOffsets(outRect, view, parent, state);
|
||||
|
||||
int itemPosition = parent.getChildAdapterPosition(view);
|
||||
if (itemPosition == RecyclerView.NO_POSITION) {
|
||||
return;
|
||||
}
|
||||
if (headerPositionCalculator.hasNewHeader(itemPosition)) {
|
||||
View header = getHeaderView(parent, itemPosition);
|
||||
setItemOffsetsForHeader(outRect, header, LinearLayout.VERTICAL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the offsets for the first item in a section to make room for the header view
|
||||
*
|
||||
* @param itemOffsets rectangle to define offsets for the item
|
||||
* @param header view used to calculate offset for the item
|
||||
* @param orientation used to calculate offset for the item
|
||||
*/
|
||||
private void setItemOffsetsForHeader(Rect itemOffsets, View header, int orientation) {
|
||||
dimensionCalculator.initMargins(rect, header);
|
||||
if (orientation == LinearLayoutManager.VERTICAL) {
|
||||
itemOffsets.top = header.getHeight() + rect.top + rect.bottom;
|
||||
} else {
|
||||
itemOffsets.left = header.getWidth() + rect.left +
|
||||
rect.right;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
|
||||
super.onDrawOver(c, parent, state);
|
||||
|
||||
final int childCount = parent.getChildCount();
|
||||
if (childCount <= 0 || adapter.getItemCount() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
View itemView = parent.getChildAt(i);
|
||||
int position = parent.getChildAdapterPosition(itemView);
|
||||
if (position == RecyclerView.NO_POSITION) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean hasStickyHeader = headerPositionCalculator.hasStickyHeader(itemView, LinearLayout.VERTICAL, position);
|
||||
if (hasStickyHeader || headerPositionCalculator.hasNewHeader(position)) {
|
||||
View header = headerProvider.getHeader(parent, position);
|
||||
//re-use existing Rect, if any.
|
||||
Rect headerOffset = headerRects.get(position);
|
||||
if (headerOffset == null) {
|
||||
headerOffset = new Rect();
|
||||
headerRects.put(position, headerOffset);
|
||||
}
|
||||
headerPositionCalculator.initHeaderBounds(headerOffset, parent, header, itemView, hasStickyHeader);
|
||||
renderer.drawHeader(parent, c, header, headerOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the position of the header under the specified (x, y) coordinates.
|
||||
*
|
||||
* @param x x-coordinate
|
||||
* @param y y-coordinate
|
||||
* @return position of header, or -1 if not found
|
||||
*/
|
||||
public int findHeaderPositionUnder(int x, int y) {
|
||||
for (int i = 0; i < headerRects.size(); i++) {
|
||||
Rect rect = headerRects.get(headerRects.keyAt(i));
|
||||
if (rect.contains(x, y)) {
|
||||
int position = headerRects.keyAt(i);
|
||||
if (visibilityAdapter == null || visibilityAdapter.isPositionVisible(position)) {
|
||||
return position;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the header view for the associated position. If it doesn't exist yet, it will be
|
||||
* created, measured, and laid out.
|
||||
*
|
||||
* @param parent the recyclerview
|
||||
* @param position the position to get the header view for
|
||||
* @return Header view
|
||||
*/
|
||||
public View getHeaderView(RecyclerView parent, int position) {
|
||||
return headerProvider.getHeader(parent, position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates cached headers. This does not invalidate the recyclerview, you should do that manually after
|
||||
* calling this method.
|
||||
*/
|
||||
public void invalidateHeaders() {
|
||||
headerProvider.invalidate();
|
||||
headerRects.clear();
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package hikapro.com.backpack.presenter.adapters.helper;
|
||||
package hikapro.com.backpack.presenter.adapters.helper.sets;
|
||||
|
||||
/**
|
||||
* Created by N551 on 25.04.2016.
|
|
@ -1,4 +1,4 @@
|
|||
package hikapro.com.backpack.presenter.adapters.helper;
|
||||
package hikapro.com.backpack.presenter.adapters.helper.sets;
|
||||
|
||||
/**
|
||||
* Created by N551 on 25.04.2016.
|
|
@ -1,4 +1,4 @@
|
|||
package hikapro.com.backpack.presenter.adapters.helper;
|
||||
package hikapro.com.backpack.presenter.adapters.helper.sets;
|
||||
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package hikapro.com.backpack.presenter.adapters.helper;
|
||||
package hikapro.com.backpack.presenter.adapters.helper.sets;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.support.v7.widget.RecyclerView;
|
|
@ -1,12 +1,11 @@
|
|||
package hikapro.com.backpack.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.widget.Toast;
|
||||
|
||||
import hikapro.com.backpack.model.entities.Item;
|
||||
import hikapro.com.backpack.model.entities.Set;
|
||||
import hikapro.com.backpack.presenter.Presenter;
|
||||
import hikapro.com.backpack.presenter.adapters.helper.OnStartDragListener;
|
||||
import hikapro.com.backpack.presenter.adapters.helper.sets.OnStartDragListener;
|
||||
|
||||
/**
|
||||
* Created by tariel on 19/04/16.
|
||||
|
|
|
@ -7,8 +7,12 @@ import android.content.Context;
|
|||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.SearchView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import hikapro.com.backpack.R;
|
||||
|
@ -18,7 +22,8 @@ import hikapro.com.backpack.presenter.ItemListPresenter;
|
|||
import hikapro.com.backpack.presenter.Presenter;
|
||||
|
||||
|
||||
public class ItemListFragment extends Fragment implements hikapro.com.backpack.view.View.ItemList {
|
||||
public class ItemListFragment extends Fragment implements hikapro.com.backpack.view.View.ItemList,
|
||||
SearchView.OnQueryTextListener {
|
||||
|
||||
private static final String BUNDLE_SET_KEY = "BUNDLE_SET_KEY";
|
||||
private hikapro.com.backpack.view.View.ActivityCallback activityCallback;
|
||||
|
@ -40,6 +45,34 @@ public class ItemListFragment extends Fragment implements hikapro.com.backpack.v
|
|||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
|
||||
inflater.inflate(R.menu.menu_main, menu);
|
||||
|
||||
final MenuItem item = menu.findItem(R.id.action_search);
|
||||
final SearchView searchView = (SearchView) item.getActionView();
|
||||
searchView.setOnQueryTextListener(this);
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
presenter.filter(newText);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
// life cycle -->
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
|
@ -65,6 +98,7 @@ public class ItemListFragment extends Fragment implements hikapro.com.backpack.v
|
|||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
Log.i(this.toString(), "onCreate");
|
||||
}
|
||||
@Override
|
||||
|
|
|
@ -6,7 +6,6 @@ import android.app.Fragment;
|
|||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
@ -14,7 +13,7 @@ import android.view.ViewGroup;
|
|||
|
||||
import hikapro.com.backpack.model.entities.Set;
|
||||
import hikapro.com.backpack.presenter.Presenter;
|
||||
import hikapro.com.backpack.presenter.adapters.helper.OnStartDragListener;
|
||||
import hikapro.com.backpack.presenter.adapters.helper.sets.OnStartDragListener;
|
||||
|
||||
public class SetListFragment extends Fragment implements hikapro.com.backpack.view.View.SetList, OnStartDragListener{
|
||||
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
package hikapro.com.backpack.view.recycler;
|
||||
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import hikapro.com.backpack.R;
|
||||
|
||||
/**
|
||||
* Created by tariel on 20/04/16.
|
||||
*/
|
||||
public class CategoryViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
public LinearLayout section;
|
||||
public TextView sectionText;
|
||||
public RecyclerView itemsRecycler;
|
||||
|
||||
public CategoryViewHolder(View v) {
|
||||
super(v);
|
||||
setupViews(v);
|
||||
}
|
||||
|
||||
private void setupViews(View view) {
|
||||
section = (LinearLayout)view.findViewById(R.id.linear);
|
||||
sectionText = (TextView)view.findViewById(R.id.section_text);
|
||||
itemsRecycler = (RecyclerView) view.findViewById(R.id.category_inner_recycler);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package hikapro.com.backpack.view.recycler;
|
||||
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import hikapro.com.backpack.R;
|
||||
|
||||
/**
|
||||
* Created by tariel on 01/05/16.
|
||||
*/
|
||||
public class HeaderViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
public int id;
|
||||
public TextView title;
|
||||
|
||||
public HeaderViewHolder(View v) {
|
||||
super(v);
|
||||
setupViews(v);
|
||||
}
|
||||
|
||||
private void setupViews(View view) {
|
||||
title = (TextView) view.findViewById(R.id.header);
|
||||
}
|
||||
}
|
|
@ -2,18 +2,21 @@ package hikapro.com.backpack.view.recycler;
|
|||
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import hikapro.com.backpack.R;
|
||||
|
||||
|
||||
/**
|
||||
* Created by tariel on 20/04/16.
|
||||
* Created by tariel on 01/05/16.
|
||||
*/
|
||||
public class ItemViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
public TextView title;
|
||||
public int id;
|
||||
public int categoryId;
|
||||
public TextView title;
|
||||
public Button undoButton;
|
||||
|
||||
public ItemViewHolder(View v) {
|
||||
super(v);
|
||||
|
@ -21,6 +24,7 @@ public class ItemViewHolder extends RecyclerView.ViewHolder {
|
|||
}
|
||||
|
||||
private void setupViews(View view) {
|
||||
title = (TextView) view.findViewById(R.id.item_text);
|
||||
title = (TextView) view.findViewById(R.id.item_txt);
|
||||
undoButton = (Button) view.findViewById(R.id.undo_button);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import android.view.View;
|
|||
import android.widget.TextView;
|
||||
|
||||
import hikapro.com.backpack.R;
|
||||
import hikapro.com.backpack.presenter.adapters.helper.ItemTouchHelperViewHolder;
|
||||
import hikapro.com.backpack.presenter.adapters.helper.sets.ItemTouchHelperViewHolder;
|
||||
|
||||
/**
|
||||
* Created by tariel on 20/04/16.
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
|
||||
</vector>
|
|
@ -6,7 +6,7 @@
|
|||
android:orientation="vertical">
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/categories_main_recycler"
|
||||
android:id="@+id/items_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:scrollbars="vertical"
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:background="@android:color/transparent"
|
||||
android:textSize="16sp"
|
||||
android:id="@+id/item_text"
|
||||
android:textStyle="bold"
|
||||
android:height="30dp"
|
||||
/>
|
|
@ -1,49 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingLeft="16dp"
|
||||
android:singleLine="true"
|
||||
android:textAllCaps="true"
|
||||
android:background="@android:color/transparent"
|
||||
android:textSize="16sp"
|
||||
android:id="@+id/section_text"
|
||||
android:textStyle="bold"/>
|
||||
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:id="@+id/linear">
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/category_inner_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
</android.support.v7.widget.RecyclerView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</RelativeLayout>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/sticky_header_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<TextView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp"
|
||||
android:background="#001F3F"
|
||||
android:textSize="28sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@android:color/white"
|
||||
tools:text="Animals starting with A"
|
||||
android:id="@+id/header"
|
||||
tools:context=".MainActivity"/>
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
>
|
||||
<TextView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="horizontal"
|
||||
android:clickable="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp"
|
||||
android:textSize="14sp"
|
||||
tools:text="Aardvark"
|
||||
android:id="@+id/item_txt"
|
||||
tools:context=".MainActivity"/>
|
||||
<Button
|
||||
android:id="@+id/undo_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Undo"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="@android:color/white"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
style="@style/Base.Widget.AppCompat.Button.Borderless"/>
|
||||
</FrameLayout>
|
|
@ -0,0 +1,20 @@
|
|||
<menu 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"
|
||||
tools:context=".MainActivity">
|
||||
<item
|
||||
android:id="@+id/action_settings"
|
||||
android:orderInCategory="1"
|
||||
android:title="Settings"
|
||||
android:showAsAction="never" />
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/action_search"
|
||||
android:icon="@android:drawable/ic_menu_search"
|
||||
android:showAsAction="always|collapseActionView"
|
||||
app:showAsAction="always|collapseActionView"
|
||||
android:actionViewClass="android.widget.SearchView"
|
||||
android:visible="true"
|
||||
android:title="Search"/>
|
||||
|
||||
</menu>
|
|
@ -2,4 +2,5 @@
|
|||
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||
<dimen name="ic_clear_margin">16dp</dimen>
|
||||
</resources>
|
||||
|
|
Loading…
Reference in New Issue