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.DetailModel;
|
||||||
import hikapro.com.backpack.model.ItemModel;
|
import hikapro.com.backpack.model.ItemModel;
|
||||||
import hikapro.com.backpack.model.SetModel;
|
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.Item;
|
||||||
import hikapro.com.backpack.model.entities.Set;
|
import hikapro.com.backpack.model.entities.Set;
|
||||||
import hikapro.com.backpack.presenter.ItemDetailPresenter;
|
import hikapro.com.backpack.presenter.ItemDetailPresenter;
|
||||||
import hikapro.com.backpack.presenter.ItemListPresenter;
|
import hikapro.com.backpack.presenter.ItemListPresenter;
|
||||||
import hikapro.com.backpack.presenter.SetListPresenter;
|
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.View;
|
||||||
import hikapro.com.backpack.view.fragments.ItemDetailFragment;
|
import hikapro.com.backpack.view.fragments.ItemDetailFragment;
|
||||||
import hikapro.com.backpack.view.fragments.ItemListFragment;
|
import hikapro.com.backpack.view.fragments.ItemListFragment;
|
||||||
|
|
|
@ -9,16 +9,12 @@ import java.util.Collections;
|
||||||
import java.util.Hashtable;
|
import java.util.Hashtable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import hikapro.com.backpack.model.database.Command;
|
import hikapro.com.backpack.model.dao.Command;
|
||||||
import hikapro.com.backpack.model.database.DAO;
|
import hikapro.com.backpack.model.dao.DAO;
|
||||||
import hikapro.com.backpack.model.database.Event;
|
import hikapro.com.backpack.model.dao.Event;
|
||||||
import hikapro.com.backpack.model.entities.Category;
|
import hikapro.com.backpack.model.entities.Category;
|
||||||
import hikapro.com.backpack.model.entities.Item;
|
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.Presenter;
|
||||||
import retrofit2.Call;
|
|
||||||
import retrofit2.Callback;
|
|
||||||
import retrofit2.Response;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by tariel on 22/04/16.
|
* Created by tariel on 22/04/16.
|
||||||
|
@ -29,19 +25,25 @@ public class ItemModel implements Model.Item {
|
||||||
private List<Category> rawCategories;
|
private List<Category> rawCategories;
|
||||||
private List<Category> sortedCategories;
|
private List<Category> sortedCategories;
|
||||||
private List<Item> rawItems;
|
private List<Item> rawItems;
|
||||||
|
|
||||||
private DAO dao;
|
private DAO dao;
|
||||||
private int currentSet;
|
private int currentSet;
|
||||||
private Category currentCategory;
|
private Hashtable<Integer, Category> categoriesCache;
|
||||||
private List<Category> categoriesCache;
|
private List<Item> itemsCache;
|
||||||
|
private List<Item> itemsDiscardCache;
|
||||||
|
|
||||||
private Hashtable<Category, List<Item>> items;
|
private Hashtable<Category, List<Item>> items;
|
||||||
private Hashtable<Integer, Hashtable<Category, List<Item>>> cache;
|
private Hashtable<Integer, List<Item>> cache;
|
||||||
|
|
||||||
public ItemModel() {
|
public ItemModel() {
|
||||||
this.rawCategories = new ArrayList<>();
|
this.rawCategories = new ArrayList<>();
|
||||||
this.rawItems = new ArrayList<>();
|
this.rawItems = new ArrayList<>();
|
||||||
this.sortedCategories = 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.cache = new Hashtable<>(12, 0.9f);
|
||||||
this.dao = DAO.getInstance();
|
this.dao = DAO.getInstance();
|
||||||
dao.registerObserver(this);
|
dao.registerObserver(this);
|
||||||
|
@ -51,8 +53,11 @@ public class ItemModel implements Model.Item {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Category getCategoryByPosition(int position) {
|
public Category getCategoryByPosition(int position) {
|
||||||
currentCategory = categoriesCache.get(position);
|
Category ret = null;
|
||||||
return currentCategory;
|
if (cache.containsKey(currentSet))
|
||||||
|
ret = categoriesCache.get(cache.get(currentSet).get(position).getCategory());
|
||||||
|
return ret;
|
||||||
|
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public int getCategoriesCount() {
|
public int getCategoriesCount() {
|
||||||
|
@ -65,12 +70,23 @@ public class ItemModel implements Model.Item {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int insertItem(Item item) {
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
@Override
|
@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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Item findItem(int id) {
|
public Item findItem(int id) {
|
||||||
Item item = null;
|
Item item = null;
|
||||||
|
@ -83,15 +99,75 @@ public class ItemModel implements Model.Item {
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public Item getItemByPosition(int categoryId, int position) {
|
public Item getItemByPosition(int position) {
|
||||||
Item ret = cache.get(currentSet).get(currentCategory).get(position);
|
Item ret = null;
|
||||||
|
if (cache.containsKey(currentSet))
|
||||||
|
ret = cache.get(currentSet).get(position);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemsCount(int categoryId) {
|
public void filter(String query) {
|
||||||
int ret = cache.get(currentSet).get(currentCategory).size();
|
|
||||||
return ret;
|
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 <--
|
// items <--
|
||||||
|
|
||||||
// events -->
|
// events -->
|
||||||
|
@ -117,17 +193,21 @@ public class ItemModel implements Model.Item {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void executeQuery() {
|
public void executeQuery() {
|
||||||
|
Message command;
|
||||||
|
|
||||||
if (cache.contains(currentSet)) {
|
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();
|
notifyDataSetChanged();
|
||||||
} else {
|
} 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.what = Command.SET_GET_ITEMS;
|
||||||
command.arg1 = presenter.getCurrentSet().getId();
|
command.arg1 = presenter.getCurrentSet().getId();
|
||||||
dao.executeCommand(command);
|
dao.executeCommand(command);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
|
@ -146,13 +226,16 @@ public class ItemModel implements Model.Item {
|
||||||
break;
|
break;
|
||||||
case Event.ITEM_INSERT_ERROR :
|
case Event.ITEM_INSERT_ERROR :
|
||||||
break;
|
break;
|
||||||
|
case Event.ITEM_CATEGORY_LOAD_ERROR :
|
||||||
|
break;
|
||||||
case Event.SET_ITEMS_LOAD_COMPLETED :
|
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);
|
cache.put(event.arg1, res);
|
||||||
Category[] array = res.keySet().toArray(new Category[res.keySet().size()]);
|
|
||||||
categoriesCache = Arrays.asList(array);
|
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
break;
|
break;
|
||||||
|
case Event.ITEM_CATEGORY_LOAD_COMPLETED :
|
||||||
|
categoriesCache = (Hashtable<Integer, Category>)event.obj;
|
||||||
|
break;
|
||||||
case Event.ITEM_FROM_SET_DELETED :
|
case Event.ITEM_FROM_SET_DELETED :
|
||||||
break;
|
break;
|
||||||
case Event.ITEM_DELETED :
|
case Event.ITEM_DELETED :
|
||||||
|
@ -220,7 +303,6 @@ public class ItemModel implements Model.Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return category;
|
return category;
|
||||||
|
|
||||||
}
|
}
|
||||||
private Category findCategory(int categoryId) {
|
private Category findCategory(int categoryId) {
|
||||||
Category category = null;
|
Category category = null;
|
||||||
|
|
|
@ -34,10 +34,20 @@ public interface Model {
|
||||||
|
|
||||||
interface Item extends Base {
|
interface Item extends Base {
|
||||||
int insertItem(hikapro.com.backpack.model.entities.Item item);
|
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 findItem(int id);
|
||||||
hikapro.com.backpack.model.entities.Item getItemByPosition(int categoryId, int position);
|
hikapro.com.backpack.model.entities.Item getItemByPosition(int position);
|
||||||
int getItemsCount(int categoryId);
|
|
||||||
hikapro.com.backpack.model.entities.Category getCategoryByPosition(int position);
|
hikapro.com.backpack.model.entities.Category getCategoryByPosition(int position);
|
||||||
int getCategoriesCount();
|
int getCategoriesCount();
|
||||||
void setPresenter(Presenter.ItemList presenter);
|
void setPresenter(Presenter.ItemList presenter);
|
||||||
|
|
|
@ -5,9 +5,9 @@ import android.os.Message;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import hikapro.com.backpack.model.database.Command;
|
import hikapro.com.backpack.model.dao.Command;
|
||||||
import hikapro.com.backpack.model.database.DAO;
|
import hikapro.com.backpack.model.dao.DAO;
|
||||||
import hikapro.com.backpack.model.database.Event;
|
import hikapro.com.backpack.model.dao.Event;
|
||||||
import hikapro.com.backpack.model.entities.Set;
|
import hikapro.com.backpack.model.entities.Set;
|
||||||
import hikapro.com.backpack.presenter.Presenter;
|
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.
|
* Created by tariel on 27/04/16.
|
||||||
|
@ -18,6 +18,7 @@ public interface Command {
|
||||||
int ITEM_INSERT = 0x79;
|
int ITEM_INSERT = 0x79;
|
||||||
int ITEM_PACK = 0x7A;
|
int ITEM_PACK = 0x7A;
|
||||||
int ITEM_UNPACK = 0x7B;
|
int ITEM_UNPACK = 0x7B;
|
||||||
|
int ITEM_GET_CATEGORIES = 0x7C;
|
||||||
|
|
||||||
int MY_LIST_POST = 0x8C;
|
int MY_LIST_POST = 0x8C;
|
||||||
int MY_LIST_ITEM_ADD = 0x8D;
|
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.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
@ -132,6 +132,12 @@ public class DAO {
|
||||||
threadPool.execute(setTask);
|
threadPool.execute(setTask);
|
||||||
break;
|
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 :
|
case Command.ITEM_INSERT :
|
||||||
itemTask = new ItemTask(Command.ITEM_INSERT,
|
itemTask = new ItemTask(Command.ITEM_INSERT,
|
||||||
Process.THREAD_PRIORITY_BACKGROUND);
|
Process.THREAD_PRIORITY_BACKGROUND);
|
||||||
|
@ -590,6 +596,16 @@ public class DAO {
|
||||||
else
|
else
|
||||||
message.what = Event.ITEM_UNPACK_ERROR;
|
message.what = Event.ITEM_UNPACK_ERROR;
|
||||||
break;
|
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);
|
handler.sendMessage(message);
|
||||||
}
|
}
|
||||||
|
@ -630,22 +646,8 @@ public class DAO {
|
||||||
message.what = Event.SET_ITEMS_LOAD_ERROR;
|
message.what = Event.SET_ITEMS_LOAD_ERROR;
|
||||||
else {
|
else {
|
||||||
Collections.sort(items);
|
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.what = Event.SET_ITEMS_LOAD_COMPLETED;
|
||||||
message.obj = result;
|
message.obj = items;
|
||||||
message.arg1 = setId;
|
message.arg1 = setId;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
|
@ -1,8 +1,7 @@
|
||||||
package hikapro.com.backpack.model.database;
|
package hikapro.com.backpack.model.dao;
|
||||||
|
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.reflect.TypeToken;
|
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.content.Context;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
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.
|
* Created by tariel on 27/04/16.
|
||||||
|
@ -24,12 +24,14 @@ public interface Event {
|
||||||
int ITEM_DELETE_ERROR = -0x16;
|
int ITEM_DELETE_ERROR = -0x16;
|
||||||
int ITEM_PACK_ERROR = -0x17;
|
int ITEM_PACK_ERROR = -0x17;
|
||||||
int ITEM_UNPACK_ERROR = -0x18;
|
int ITEM_UNPACK_ERROR = -0x18;
|
||||||
|
int ITEM_CATEGORY_LOAD_ERROR = -0x19;
|
||||||
|
|
||||||
int ITEM_FROM_SET_DELETED = 0x14;
|
int ITEM_FROM_SET_DELETED = 0x14;
|
||||||
int ITEM_INSERTED = 0x15;
|
int ITEM_INSERTED = 0x15;
|
||||||
int ITEM_DELETED = 0x16;
|
int ITEM_DELETED = 0x16;
|
||||||
int ITEM_PACKED = 0x17;
|
int ITEM_PACKED = 0x17;
|
||||||
int ITEM_UNPACKED = 0x18;
|
int ITEM_UNPACKED = 0x18;
|
||||||
|
int ITEM_CATEGORY_LOAD_COMPLETED = 0x19;
|
||||||
|
|
||||||
int MY_LIST_POST_ERROR = -0x28;
|
int MY_LIST_POST_ERROR = -0x28;
|
||||||
int MY_LIST_ITEM_ADD_ERROR = -0x29;
|
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.
|
* Created by tariel on 27/04/16.
|
|
@ -2,27 +2,24 @@ package hikapro.com.backpack.presenter;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v7.widget.DefaultItemAnimator;
|
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import hikapro.com.backpack.R;
|
import hikapro.com.backpack.R;
|
||||||
import hikapro.com.backpack.model.ItemModel;
|
|
||||||
import hikapro.com.backpack.model.Model;
|
import hikapro.com.backpack.model.Model;
|
||||||
import hikapro.com.backpack.model.entities.Item;
|
import hikapro.com.backpack.model.entities.Item;
|
||||||
import hikapro.com.backpack.model.entities.Set;
|
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.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.View;
|
||||||
import hikapro.com.backpack.view.recycler.CategoryViewHolder;
|
|
||||||
import hikapro.com.backpack.view.recycler.ItemViewHolder;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by tariel on 20/04/16.
|
* Created by tariel on 20/04/16.
|
||||||
|
@ -34,10 +31,12 @@ public class ItemListPresenter implements Presenter.ItemList {
|
||||||
private WeakReference<View.ItemList> view;
|
private WeakReference<View.ItemList> view;
|
||||||
private Model.Item model;
|
private Model.Item model;
|
||||||
private Set set;
|
private Set set;
|
||||||
private CategoryListAdapter categoryListAdapter;
|
private ItemListAdapter adapter;
|
||||||
|
RecyclerView recycler;
|
||||||
|
|
||||||
public ItemListPresenter() {
|
public ItemListPresenter() {
|
||||||
this.categoryListAdapter = new CategoryListAdapter(this);
|
this.adapter = new ItemListAdapter(this);
|
||||||
|
adapter.setHasStableIds(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// life cycle -->
|
// life cycle -->
|
||||||
|
@ -56,10 +55,25 @@ public class ItemListPresenter implements Presenter.ItemList {
|
||||||
set = (Set) savedInstanceState.getSerializable(BUNDLE_SET_LIST_KEY);
|
set = (Set) savedInstanceState.getSerializable(BUNDLE_SET_LIST_KEY);
|
||||||
android.view.View view = inflater.inflate(R.layout.fragment_item_list, container, false);
|
android.view.View view = inflater.inflate(R.layout.fragment_item_list, container, false);
|
||||||
LinearLayoutManager llm = new LinearLayoutManager(getActivityContext());
|
LinearLayoutManager llm = new LinearLayoutManager(getActivityContext());
|
||||||
RecyclerView mainRecycler = (RecyclerView) view.findViewById(R.id.categories_main_recycler);
|
recycler = (RecyclerView) view.findViewById(R.id.items_recycler);
|
||||||
mainRecycler.setLayoutManager(llm);
|
recycler.setLayoutManager(llm);
|
||||||
mainRecycler.setAdapter(categoryListAdapter);
|
recycler.setAdapter(adapter);
|
||||||
mainRecycler.setItemAnimator(new DefaultItemAnimator());
|
|
||||||
|
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();
|
model.executeQuery();
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
@ -71,68 +85,11 @@ public class ItemListPresenter implements Presenter.ItemList {
|
||||||
|
|
||||||
// life cycle <--
|
// 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 -->
|
// process -->
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void notifyDataSetChanged() {
|
public void notifyDataSetChanged() {
|
||||||
categoryListAdapter.notifyDataSetChanged();
|
adapter.notifyDataSetChanged();
|
||||||
categoryListAdapter.notifyItemAdapters();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -156,6 +113,12 @@ public class ItemListPresenter implements Presenter.ItemList {
|
||||||
public void setModel(Model.Item model) {
|
public void setModel(Model.Item model) {
|
||||||
this.model = model;
|
this.model = model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Model.Item getModel() {
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Context getAppContext() {
|
public Context getAppContext() {
|
||||||
try {
|
try {
|
||||||
|
@ -186,6 +149,11 @@ public class ItemListPresenter implements Presenter.ItemList {
|
||||||
throw new NullPointerException("View is unavailable");
|
throw new NullPointerException("View is unavailable");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void filter(String query) {
|
||||||
|
adapter.filter(query);
|
||||||
|
recycler.scrollToPosition(0);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set getCurrentSet() {
|
public Set getCurrentSet() {
|
||||||
|
|
|
@ -3,15 +3,12 @@ package hikapro.com.backpack.presenter;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.view.DragEvent;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import hikapro.com.backpack.model.Model;
|
import hikapro.com.backpack.model.Model;
|
||||||
import hikapro.com.backpack.model.entities.Item;
|
import hikapro.com.backpack.model.entities.Item;
|
||||||
import hikapro.com.backpack.model.entities.Set;
|
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.DetailViewHolder;
|
||||||
import hikapro.com.backpack.view.recycler.ItemViewHolder;
|
import hikapro.com.backpack.view.recycler.ItemViewHolder;
|
||||||
import hikapro.com.backpack.view.recycler.SetViewHolder;
|
import hikapro.com.backpack.view.recycler.SetViewHolder;
|
||||||
|
@ -45,21 +42,18 @@ public interface Presenter {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ItemList extends Base {
|
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);
|
void onDestroy(boolean isChangingConfiguration);
|
||||||
android.view.View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState);
|
android.view.View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState);
|
||||||
void setView(hikapro.com.backpack.view.View.ItemList view);
|
void setView(hikapro.com.backpack.view.View.ItemList view);
|
||||||
void setModel(Model.Item model);
|
void setModel(Model.Item model);
|
||||||
|
Model.Item getModel();
|
||||||
void notifyDataSetChanged();
|
void notifyDataSetChanged();
|
||||||
Set getCurrentSet();
|
Set getCurrentSet();
|
||||||
void showMessage(String message);
|
void showMessage(String message);
|
||||||
void onSaveInstanceState(Bundle outState);
|
void onSaveInstanceState(Bundle outState);
|
||||||
void clickItem(int itemId);
|
void clickItem(int itemId);
|
||||||
|
void filter(String query);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ItemDetail extends Base {
|
interface ItemDetail extends Base {
|
||||||
|
|
|
@ -17,8 +17,8 @@ import hikapro.com.backpack.R;
|
||||||
import hikapro.com.backpack.model.Model;
|
import hikapro.com.backpack.model.Model;
|
||||||
import hikapro.com.backpack.model.entities.Set;
|
import hikapro.com.backpack.model.entities.Set;
|
||||||
import hikapro.com.backpack.presenter.adapters.SetListAdapter;
|
import hikapro.com.backpack.presenter.adapters.SetListAdapter;
|
||||||
import hikapro.com.backpack.presenter.adapters.helper.OnStartDragListener;
|
import hikapro.com.backpack.presenter.adapters.helper.sets.OnStartDragListener;
|
||||||
import hikapro.com.backpack.presenter.adapters.helper.SimpleItemTouchHelperCallback;
|
import hikapro.com.backpack.presenter.adapters.helper.sets.SimpleItemTouchHelperCallback;
|
||||||
import hikapro.com.backpack.view.View;
|
import hikapro.com.backpack.view.View;
|
||||||
import hikapro.com.backpack.view.recycler.SetViewHolder;
|
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;
|
package hikapro.com.backpack.presenter.adapters;
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.os.Handler;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
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.Presenter;
|
||||||
|
import hikapro.com.backpack.presenter.adapters.helper.items.StickyHeaderAdapter;
|
||||||
|
import hikapro.com.backpack.view.recycler.HeaderViewHolder;
|
||||||
import hikapro.com.backpack.view.recycler.ItemViewHolder;
|
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 Presenter.ItemList presenter;
|
||||||
private int categoryId;
|
|
||||||
|
|
||||||
public ItemListAdapter(Presenter.ItemList presenter) {
|
public ItemListAdapter(Presenter.ItemList presenter) {
|
||||||
this.presenter = presenter;
|
this.presenter = presenter;
|
||||||
|
@ -20,19 +37,145 @@ public class ItemListAdapter extends RecyclerView.Adapter<ItemViewHolder> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount() {
|
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
|
@Override
|
||||||
public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
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
|
@Override
|
||||||
public void onBindViewHolder(ItemViewHolder holder, int position) {
|
public void setHasStableIds(boolean hasStableIds) {
|
||||||
presenter.bindViewHolderItem(holder, position, categoryId);
|
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 android.view.ViewGroup;
|
||||||
|
|
||||||
import hikapro.com.backpack.presenter.SetListPresenter;
|
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;
|
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.
|
* 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.
|
* 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;
|
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.graphics.Canvas;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
|
@ -1,12 +1,11 @@
|
||||||
package hikapro.com.backpack.view;
|
package hikapro.com.backpack.view;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import hikapro.com.backpack.model.entities.Item;
|
import hikapro.com.backpack.model.entities.Item;
|
||||||
import hikapro.com.backpack.model.entities.Set;
|
import hikapro.com.backpack.model.entities.Set;
|
||||||
import hikapro.com.backpack.presenter.Presenter;
|
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.
|
* Created by tariel on 19/04/16.
|
||||||
|
|
|
@ -7,8 +7,12 @@ import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.SearchView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import hikapro.com.backpack.R;
|
import hikapro.com.backpack.R;
|
||||||
|
@ -18,7 +22,8 @@ import hikapro.com.backpack.presenter.ItemListPresenter;
|
||||||
import hikapro.com.backpack.presenter.Presenter;
|
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 static final String BUNDLE_SET_KEY = "BUNDLE_SET_KEY";
|
||||||
private hikapro.com.backpack.view.View.ActivityCallback activityCallback;
|
private hikapro.com.backpack.view.View.ActivityCallback activityCallback;
|
||||||
|
@ -40,6 +45,34 @@ public class ItemListFragment extends Fragment implements hikapro.com.backpack.v
|
||||||
return ret;
|
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 -->
|
// life cycle -->
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Context context) {
|
public void onAttach(Context context) {
|
||||||
|
@ -65,6 +98,7 @@ public class ItemListFragment extends Fragment implements hikapro.com.backpack.v
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
setHasOptionsMenu(true);
|
||||||
Log.i(this.toString(), "onCreate");
|
Log.i(this.toString(), "onCreate");
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -6,7 +6,6 @@ import android.app.Fragment;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
@ -14,7 +13,7 @@ import android.view.ViewGroup;
|
||||||
|
|
||||||
import hikapro.com.backpack.model.entities.Set;
|
import hikapro.com.backpack.model.entities.Set;
|
||||||
import hikapro.com.backpack.presenter.Presenter;
|
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{
|
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.support.v7.widget.RecyclerView;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import hikapro.com.backpack.R;
|
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 class ItemViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
|
||||||
public TextView title;
|
|
||||||
public int id;
|
public int id;
|
||||||
public int categoryId;
|
public int categoryId;
|
||||||
|
public TextView title;
|
||||||
|
public Button undoButton;
|
||||||
|
|
||||||
public ItemViewHolder(View v) {
|
public ItemViewHolder(View v) {
|
||||||
super(v);
|
super(v);
|
||||||
|
@ -21,6 +24,7 @@ public class ItemViewHolder extends RecyclerView.ViewHolder {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupViews(View view) {
|
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 android.widget.TextView;
|
||||||
|
|
||||||
import hikapro.com.backpack.R;
|
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.
|
* 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:orientation="vertical">
|
||||||
|
|
||||||
<android.support.v7.widget.RecyclerView
|
<android.support.v7.widget.RecyclerView
|
||||||
android:id="@+id/categories_main_recycler"
|
android:id="@+id/items_recycler"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:scrollbars="vertical"
|
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. -->
|
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||||
<dimen name="activity_horizontal_margin">16dp</dimen>
|
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||||
<dimen name="activity_vertical_margin">16dp</dimen>
|
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||||
|
<dimen name="ic_clear_margin">16dp</dimen>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in New Issue