I have implemented GalleryView. I want to display a border image on selected image from Gallery.
Gallery ga = (Gallery)findViewById(R.id.Gallery01);
ga.setAdapter(new ImageAdapter(this));//, android.R.layout.simple_list_item_1, items));
imageView = (ImageView)findViewById(R.id.ImageView01);
ga.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapter, View view, int location,
long arg3) {
imageView.setImageResource(items.get(location));
final ImageView iv = (ImageView) adapter.getSelectedView();
iv.setBackgroundResource(R.drawable.large_button_sel_liner);
}
});
And my Adapter class
class ImageAdapter1 extends ArrayAdapter<Integer> {
private Context ctx;
private List<Integer> items;
public ImageAdapter1(Context context, int textViewResourceId,
List<Integer> objects) {
super(context, textViewResourceId, objects);
items = objects;
ctx = context;
}
#Override
public int getCount() {
return items.size();
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final ImageView iv = new ImageView(ctx);
iv.setImageResource(items.get(position));
iv.setScaleType(ImageView.ScaleType.FIT_XY);
iv.setLayoutParams(new Gallery.LayoutParams(150,120));
return iv;
}
}!
It is totally mashed up.
I have got the solution using Selector.
I have created galleryselector.xml
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true"
android:drawable="#drawable/large_button_sel_liner"/>
<item android:drawable="#android:color/transparent" /></selector>
and set it in Adapter class in getView():
imageView.setBackgroundDrawable(getResources().getDrawable(R.drawable.galleryselector));
Inspired by Nishant Shan's reply, I elaborate my own solution:
First of all, create a border resource: common_galleryborder_shape.xml:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<corners
android:radius="2dp"
/>
<solid
android:color="#android:color/transparent"
/>
<stroke
android:width="2dp"
android:color="#android:color/black"
/>
</shape>
Then create a selector that use this shape: common_gallerycurrentitem_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:state_selected="true"
android:drawable="#drawable/common_galleryborder_shape"
/>
<item
android:drawable="#android:color/transparent"
/>
</selector>
And, finally, add this code to your adapter class:
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView = new ImageView(mContext);
Integer drawableId = items.get(position);
Drawable drawable = ctx.getResources().getDrawable(drawableId);
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
imageView.setImageResource(drawableId);
//sets image size to same size of true image
imageView.setLayoutParams(new Gallery.LayoutParams(width, height));
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
//add a padding for the border
int padding = dipToPx(mContext, 2);
imageView.setPadding(padding, padding, padding, padding);
imageView.setBackgroundResource(R.drawable.common_gallerycurrentitem_selector);
return imageView;
}
// ----------------------------------------- Private Methods
/**
* Convert a dimension in dip to px
* #param context
* #param dip
* #return px
*/
private int dipToPx(Context context, int dip) {
return (int) (dip * context.getResources().getDisplayMetrics().density);
}
In addition, is also possible set Gallery.setSpacing(int) value to avoid overlapping images inside the gallery.
What has worked for me in the past is to create an Integer that tracks the click selection, and then apply the background in the getView() method if the position matches the click selection. So (roughly):
Integer selectedLocation = null;
#Override
public void onItemClick(AdapterView<?> adapter, View view, int location,
long arg3) {
selectedLocation = location;
}
and then in the Adapter View:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final ImageView iv = new ImageView(ctx);
iv.setImageResource(items.get(position));
iv.setScaleType(ImageView.ScaleType.FIT_XY);
iv.setLayoutParams(new Gallery.LayoutParams(150,120));
if(position == selectedLocation){
iv.setBackgroundResource(R.drawable.large_button_sel_liner);
}else{
iv.setBackgroundResource(0);
}
return iv;
}
You might have to do a bit more work to track the right selection (the click location and the position might not be the right variables to use), but otherwise something similar should work.
Related
I am trying to set a dynamic width and height of my GridView's items, this is my code:
class GridAdapter extends BaseAdapter {
private Context context;
private GridAdapter(Context context, List<ParseObject> objects) {
super();
this.context = context;
}
// CONFIGURE CELL
#Override
public View getView(int position, View cell, ViewGroup parent) {
if (cell == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
cell = inflater.inflate(R.layout.cell_post_search, null);
}
//-----------------------------------------------
// MARK - INITIALIZE VIEWS
//-----------------------------------------------
ImageView postImg = cell.findViewById(R.id.cpsPostImg);
ImageView videoicon = cell.findViewById(R.id.cpsVideoIcon);
...
return cell;
}
#Override public int getCount() { return postsArray.size(); }
#Override public Object getItem(int position) { return postsArray.get(position); }
#Override public long getItemId(int position) { return position; }
}
// Set Adapter
postsGridView.setAdapter(new GridAdapter(ctx, postsArray));
// Set number of Columns accordingly to the device used
float scalefactor = getResources().getDisplayMetrics().density * screenW/3; // LET'S PRETEND MY screenW = 720, this value whoudl, be 240, which is the width i need for my cell
int number = getWindowManager().getDefaultDisplay().getWidth();
int columns = (int) ((float) number / scalefactor);
postsGridView.setNumColumns(columns);
Log.i(Configurations.TAG, "SCALE FACTOR: " + scalefactor);
And here's my custom cell_post_search.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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="match_parent">
<RelativeLayout
android:id="#+id/cpsCellLayout"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true">
<ImageView
android:id="#+id/cpsPostImg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:background="#c1c1c1"
android:scaleType="centerCrop"/>
<ImageView
android:id="#+id/cpsVideoIcon"
android:layout_width="44dp"
android:layout_height="44dp"
android:layout_alignEnd="#+id/cpsPostImg"
android:layout_alignParentTop="true"
android:layout_marginRight="5dp"
android:layout_marginTop="5dp"
android:visibility="invisible"
app:srcCompat="#drawable/play_butt"/>
<ImageView
android:id="#+id/cpsWhiteFrame"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:srcCompat="#drawable/white_frame"/>
</RelativeLayout>
</RelativeLayout>
I get 480.0 as scale factor in my Logcat, which is not the right size I need (in my case it should be 240). Anyway, I've also tried to do this:
int columns = (int) ((float) number / (scalefactor/2));
So the scalefactor = 240, but it doesn't matter, because I need my cell's item to be square size, so basically: WIDTH = screenWidth/3, HEIGHT = screenWidth/3.
It doesn't work properly, my GridView shows 3 columns but cells get stretched in width - height looks fine - as shown here:
Is there a way to edit my code and make cells size correctly, as square images, 3 columns, based on the device size?
Try This
class GridAdapter extends BaseAdapter {
private Context context;
private GridAdapter(Context context, List<ParseObject> objects) {
super();
this.context = context;
}
// CONFIGURE CELL
#Override
public View getView(int position, View cell, ViewGroup parent) {
if (cell == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
cell = inflater.inflate(R.layout.cell_post_search, null);
}
//-----------------------------------------------
// MARK - INITIALIZE VIEWS
//-----------------------------------------------
ImageView postImg = cell.findViewById(R.id.cpsPostImg);
ImageView videoicon = cell.findViewById(R.id.cpsVideoIcon);
DisplayMetrics displayMetrics = new DisplayMetrics();
((Activity) mContext).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int width = displayMetrics.widthPixels;
postImg.getLayoutParams().height = width / 3;
...
return cell;
}
#Override public int getCount() { return postsArray.size(); }
#Override public Object getItem(int position) { return postsArray.get(position); }
#Override public long getItemId(int position) { return position; }
}
Also, in your XML layout, add android:numColumns="3":
<GridView
android:id="#+id/upPostsGridView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:numColumns="3"/>
I have a view that uses the ExpandableListView that has a ton of logic around it and in the adapters. For e.g., it looks like this
I have a requirement to display the same view with a different skin that has the expand/collapse hidden and has a border around parent and its children, something like this
I see attributes to have border for the whole control or just parent or individual child but nothing to have a border around parent and its children.
Has anyone done something like this? Short of not using Expandablelistview and recreating the view, is there anyway I can achieve the border?
Edit 1:
Here is a gist that has the template for what I am trying to do.
Edit 2:
I have a solution playing with parent and child borders,
setting parent to ┎─┒
and all-but-last children to ┃ ┃
and last child to ┖─┚
Here is the gist for the solution I have so far
I am still open to a better solution and will offer the bounty to anything that is less kludge than my solution.
EDIT So I've added ItemDecoration feature to ExpandableListView, It's pretty much works like the RecyclerView's ItemDecoration, here is the code:
Subclass the ExpandableListView
public class ExpandableListViewItemDecoration extends ExpandableListView {
private List<ItemDecorationListView> itemDecorations = new ArrayList<>(1);
/* ... */
public void addItemDecoration(ItemDecorationListView item){
itemDecorations.add(item);
}
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
final int count = itemDecorations.size();
for (int i = 0; i < count; i++) {
itemDecorations.get(i).onDrawOver(canvas, this);
}
}
ItemDecorationListView:
public abstract class ItemDecorationListView {
public void onDrawOver(Canvas c, ListView parent) {
}
}
The ItemDecorator:
public class ItemDecoratorBorderListView extends ItemDecorationListView {
private final Paint paint = new Paint();
private final int size;
public ItemDecoratorBorderListView(int size, #ColorInt int color) {
this.size = size;
paint.setColor(color);
paint.setStrokeWidth(size);
paint.setStyle(Paint.Style.STROKE);
}
public static final String TAG = ItemDecoratorBorderListView.class.getSimpleName();
#Override
public void onDrawOver(Canvas c, ListView parent) {
super.onDrawOver(c, parent);
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
if (isHeader(child, parent, i)) {
for (int j = i + 1; j < childCount; j++) {
View childEnd = parent.getChildAt(j);
boolean end = isHeader(childEnd, parent, i) || j == childCount - 1;
if (end) {
if (BuildConfig.DEBUG) { Log.d(TAG, String.format(Locale.ENGLISH, "Draw called i: %d, j: %d", i, j)); }
childEnd = parent.getChildAt(j - 1);
if (j == childCount - 1) { childEnd = parent.getChildAt(j); }
float top = child.getTop() + child.getTranslationY() + size + child.getPaddingTop();
float bottom = childEnd.getBottom() + childEnd.getTranslationY() - size - childEnd
.getPaddingBottom();
float right = child.getRight() + child.getTranslationX() - size - child.getPaddingRight();
float left = child.getLeft() + child.getTranslationX() + size + child.getPaddingLeft();
c.drawRect(left, top, right, bottom, paint);
i = j - 1;
break;
}
}
}
}
}
public boolean isHeader(View child, ListView parent, int position) {
//You need to set an Id for your layout
return child.getId() == R.id.header;
}
}
And just add it to your ExpandableListView:
expandableList.addItemDecoration(new ItemDecoratorBorderListView(
getResources().getDimensionPixelSize(R.dimen.stroke_size),
Color.GRAY
));
Old Answer:
This is an implementation with RecyclerView and ItemDecoration, I've already written this solution before knowing you're stuck with legacy code, So I'm sharing this anyway.
Item Decoration:
public class ItemDecoratorBorder extends RecyclerView.ItemDecoration {
private final Paint paint = new Paint();
private final int size;
public ItemDecoratorBorder(int size, #ColorInt int color) {
this.size = size;
paint.setColor(color);
paint.setStrokeWidth(size);
paint.setStyle(Paint.Style.STROKE);
}
public static final String TAG = ItemDecoratorBorder.class.getSimpleName();
#Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
if (parent.getLayoutManager() == null) { return; }
int childCount = parent.getChildCount();
RecyclerView.LayoutManager lm = parent.getLayoutManager();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
if (isHeader(child, parent)) {
for (int j = i + 1; j < childCount; j++) {
View childEnd = parent.getChildAt(j);
boolean end = isHeader(childEnd, parent) || j == childCount - 1;
if (end) {
if (BuildConfig.DEBUG) { Log.d(TAG, String.format(Locale.ENGLISH, "Draw called i: %d, j: %d", i, j)); }
childEnd = parent.getChildAt(j - 1);
if (j == childCount - 1) {
childEnd = parent.getChildAt(j);
}
float top = child.getTop() + child.getTranslationY() + size + child.getPaddingTop();
float bottom = lm.getDecoratedBottom(childEnd) + childEnd.getTranslationY() - size - childEnd.getPaddingBottom();
float right = lm.getDecoratedRight(child) + child.getTranslationX() - size - child.getPaddingRight();
float left = lm.getDecoratedLeft(child) + child.getTranslationX() + size + child.getPaddingLeft();
c.drawRect(left, top, right, bottom, paint);
i = j - 1;
break;
}
}
}
}
}
public boolean isHeader(View child, RecyclerView parent) {
int viewType = parent.getLayoutManager().getItemViewType(child);
return viewType == R.layout.layout_header;
}
I'm finding where a group starts and ends using the view types and draw a rectangle around the start and end position.
The code is available at my github repo
Well I have a solution for you, but It's better to use recycleView instead of listView, However, We can draw line for every sides e.g:
for parent group it will be something like ┎─┒ and for all child's without the last child it will be something like: ┎ ┒ and for the last child it will be like : ──.
The code: `groupbg.xml:
<?xml version="1.0" encoding="UTF-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="#000" />
</shape>
</item>
<item android:left="2dp" android:top="2dp" android:right="2dp">
<shape android:shape="rectangle">
<solid android:color="#fff" />
</shape>
</item>
</layer-list>
normalchild.xml:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="#000" />
</shape>
</item>
<item android:left="2dp" android:right="2dp">
<shape android:shape="rectangle">
<solid android:color="#fff" />
</shape>
</item>
bottomchild.xml:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="#000" />
</shape>
</item>
<item android:bottom="2dp">
<shape android:shape="rectangle">
<solid android:color="#fff" />
</shape>
</item>
No set it to your adapter:
private int childrenCount;
#Override
public int getChildrenCount(int groupPosition) {
return childrenCount = data.get(groupPosition).getItems().length;
}
#Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
View view;
if (convertView == null){
view = LayoutInflater.from(context).inflate(R.layout.item, parent, false);
}
else {
view = convertView;
}
view.setBackground(context.getResources().getDrawable(R.drawable.groupbg));
TextView lblNumber = view.findViewById(R.id.lblNumber);
TextView lblName = view.findViewById(R.id.lblName);
lblNumber.setText((groupPosition + 1) + ".");
lblName.setText(((TestModel)getGroup(groupPosition)).getCategory());
return view;
}
#Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
View view;
if (convertView == null){
view = LayoutInflater.from(context).inflate(R.layout.item_child, parent, false);
}
else {
view = convertView;
}
TextView lblNumber = view.findViewById(R.id.lblNumber);
TextView lblName = view.findViewById(R.id.lblName);
lblNumber.setText((childPosition + 1)+ ".");
lblName.setText((String)getChild(groupPosition, childPosition));
if (childPosition < childrenCount)
view.setBackground(context.getResources().getDrawable(R.drawable.normalchild));
else view.setBackground(context.getResources().getDrawable(R.drawable.bottomchild));
return view;
}
You can try using this library. Custom RecyclerView that implement features like ExpandableListView.
RecyclerView by default, does come with a nice deletion animation, as long as you setHasStableIds(true) and provide correct implementation on getItemId.
Recently, I had added divider into RecyclerView via https://stackoverflow.com/a/27037230/72437
The outcome looks as following
https://www.youtube.com/watch?v=u-2kPZwF_0w
https://youtu.be/c81OsFAL3zY (To make the dividers more visible when delete animation played, I temporary change the RecyclerView background to red)
The dividers are still visible, when deletion animation being played.
However, if I look at GMail example, when deletion animation being played, divider lines are no longer visible. They are being covered a solid color area.
https://www.youtube.com/watch?v=cLs7paU-BIg
May I know, how can I achieve the same effect as GMail, by not showing divider lines, when deletion animation played?
The solution is fairly easy. To animate a decoration, you can and should use view.getTranslation_() and view.getAlpha(). I wrote a blog post some time ago on this exact issue, you can read it here.
Translation and fading off
The default layout manager will fade views out (alpha) and translate them, when they get added or removed. You have to account for this in your decoration.
The idea is simple:
However you draw your decoration, apply the same alpha and translation to your drawing by using view.getAlpha() and view.getTranslationY().
Following your linked answer, it would have to be adapted like the following:
// translate
int top = child.getBottom() + params.bottomMargin + view.getTranslationY();
int bottom = top + mDivider.getIntrinsicHeight();
// apply alpha
mDivider.setAlpha((int) child.getAlpha() * 255f);
mDivider.setBounds(left + view.getTranslationX(), top,
right + view.getTranslationX(), bottom);
mDivider.draw(c);
A complete sample
I like to draw things myself, since I think drawing a line is less overhead than layouting a drawable, this would look like the following:
public class SeparatorDecoration extends RecyclerView.ItemDecoration {
private final Paint mPaint;
private final int mAlpha;
public SeparatorDecoration(#ColorInt int color, float width) {
mPaint = new Paint();
mPaint.setColor(color);
mPaint.setStrokeWidth(width);
mAlpha = mPaint.getAlpha();
}
#Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
// we retrieve the position in the list
final int position = params.getViewAdapterPosition();
// add space for the separator to the bottom of every view but the last one
if (position < state.getItemCount()) {
outRect.set(0, 0, 0, (int) mPaint.getStrokeWidth()); // left, top, right, bottom
} else {
outRect.setEmpty(); // 0, 0, 0, 0
}
}
#Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
// a line will draw half its size to top and bottom,
// hence the offset to place it correctly
final int offset = (int) (mPaint.getStrokeWidth() / 2);
// this will iterate over every visible view
for (int i = 0; i < parent.getChildCount(); i++) {
final View view = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
// get the position
final int position = params.getViewAdapterPosition();
// and finally draw the separator
if (position < state.getItemCount()) {
// apply alpha to support animations
mPaint.setAlpha((int) (view.getAlpha() * mAlpha));
float positionY = view.getBottom() + offset + view.getTranslationY();
// do the drawing
c.drawLine(view.getLeft() + view.getTranslationX(),
positionY,
view.getRight() + view.getTranslationX(),
positionY,
mPaint);
}
}
}
}
Firstly, sorry for the massive answer size. However, I felt it necessary to include my entire test Activity so that you can see what I have done.
The issue
The issue that you have, is that the DividerItemDecoration has no idea of the state of your row. It does not know whether the item is being deleted.
For this reason, I made a POJO that we can use to contain an integer (that we use as both an itemId and a visual representation and a boolean indicating that this row is being deleted or not.
When you decide to delete entries (in this example adapter.notifyItemRangeRemoved(3, 8);), you must also set the associated Pojo to being deleted (in this example pojo.beingDeleted = true;).
The position of the divider when beingDeleted, is reset to the colour of the parent view. In order to cover up the divider.
I am not very fond of using the dataset itself to manage the state of its parent list. There is perhaps a better way.
The result visualized
The Activity:
public class MainActivity extends AppCompatActivity {
private static final int VERTICAL_ITEM_SPACE = 8;
private List<Pojo> mDataset = new ArrayList<Pojo>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
for(int i = 0; i < 30; i++) {
mDataset.add(new Pojo(i));
}
final RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.addItemDecoration(new VerticalSpaceItemDecoration(VERTICAL_ITEM_SPACE));
recyclerView.addItemDecoration(new DividerItemDecoration(this));
RecyclerView.ItemAnimator ia = recyclerView.getItemAnimator();
ia.setRemoveDuration(4000);
final Adapter adapter = new Adapter(mDataset);
recyclerView.setAdapter(adapter);
(new Handler(Looper.getMainLooper())).postDelayed(new Runnable() {
#Override
public void run() {
int index = 0;
Iterator<Pojo> it = mDataset.iterator();
while(it.hasNext()) {
Pojo pojo = it.next();
if(index >= 3 && index <= 10) {
pojo.beingDeleted = true;
it.remove();
}
index++;
}
adapter.notifyItemRangeRemoved(3, 8);
}
}, 2000);
}
public class Adapter extends RecyclerView.Adapter<Holder> {
private List<Pojo> mDataset;
public Adapter(#NonNull final List<Pojo> dataset) {
setHasStableIds(true);
mDataset = dataset;
}
#Override
public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_cell, parent, false);
return new Holder(view);
}
#Override
public void onBindViewHolder(final Holder holder, final int position) {
final Pojo data = mDataset.get(position);
holder.itemView.setTag(data);
holder.textView.setText("Test "+data.dataItem);
}
#Override
public long getItemId(int position) {
return mDataset.get(position).dataItem;
}
#Override
public int getItemCount() {
return mDataset.size();
}
}
public class Holder extends RecyclerView.ViewHolder {
public TextView textView;
public Holder(View itemView) {
super(itemView);
textView = (TextView) itemView.findViewById(R.id.text);
}
}
public class Pojo {
public int dataItem;
public boolean beingDeleted = false;
public Pojo(int dataItem) {
this.dataItem = dataItem;
}
}
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private final int[] ATTRS = new int[]{android.R.attr.listDivider};
private Paint mOverwritePaint;
private Drawable mDivider;
/**
* Default divider will be used
*/
public DividerItemDecoration(Context context) {
final TypedArray styledAttributes = context.obtainStyledAttributes(ATTRS);
mDivider = styledAttributes.getDrawable(0);
styledAttributes.recycle();
initializePaint();
}
/**
* Custom divider will be used
*/
public DividerItemDecoration(Context context, int resId) {
mDivider = ContextCompat.getDrawable(context, resId);
initializePaint();
}
private void initializePaint() {
mOverwritePaint = new Paint();
mOverwritePaint.setColor(ContextCompat.getColor(MainActivity.this, android.R.color.background_light));
}
#Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
int left = parent.getPaddingLeft();
int right = parent.getWidth() - parent.getPaddingRight();
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
int top = child.getBottom() + params.bottomMargin;
int bottom = top + mDivider.getIntrinsicHeight();
Pojo item = (Pojo) child.getTag();
if(item.beingDeleted) {
c.drawRect(left, top, right, bottom, mOverwritePaint);
} else {
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
}
}
public class VerticalSpaceItemDecoration extends RecyclerView.ItemDecoration {
private final int mVerticalSpaceHeight;
public VerticalSpaceItemDecoration(int mVerticalSpaceHeight) {
this.mVerticalSpaceHeight = mVerticalSpaceHeight;
}
#Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
RecyclerView.State state) {
outRect.bottom = mVerticalSpaceHeight;
}
}
}
The Activity Layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin"
android:background="#android:color/background_light"
tools:context="test.dae.myapplication.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
The RecyclerView "row" Layout
<?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:id="#+id/text"
android:padding="8dp">
</TextView>
I think the ItemDecorator you use to draw a divider after every row is messing things up when swipe to delete is performed.
Instead of Using ItemDecorator to draw a Divider in a recyclerview, add a view at the end of your RecyclerView child layout design.which will draw a divider line like ItemDecorator.
<LinearLayout
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="wrap_content"
>
<!-- child layout Design !-->
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#android:color/darker_gray"
android:layout_gravity="bottom"
/>
</Linearlayout>
I'm trying to make a square board with 5 x 5 grid using gridview. Here is my code:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<GridView
android:id="#+id/squareBoard"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:numColumns="5"
/>
</RelativeLayout>
This is the Adapter I used:
private class TextViewAdapter extends BaseAdapter {
private Context mContext;
private int mWidth, mHeight;
public TextViewAdapter(Context c, int width, int height) {
mContext = c;
mWidth = width;
mHeight = height;
}
public int getCount() {
return mWidth * mHeight;
}
public TextView getItem(int position) {
return null;
}
public long getItemId(int position) {
return 0;
}
// create a new ImageView for each item referenced by the Adapter
public View getView(int position, View convertView, ViewGroup parent) {
TextView tv = null;
if (convertView == null) { // if it's not recycled, initialize some attributes
tv = new TextView(mContext);
tv.setText("" + position);
tv.setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL);
tv.setLayoutParams(new GridView.LayoutParams(100, 100));
tv.setWidth(100);
tv.setBackground(getResources().getDrawable(R.drawable.letter_box));
} else {
tv = (TextView) convertView;
}
return tv;
}
}
and this is the letter_box.xml of the textview background
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid
android:color="#android:color/white" >
</solid>
<stroke
android:width="1dp"
android:color="#android:color/black" >
</stroke>
</shape>
So far, i managed to display boxes, but there's always horizontal space between columns. I want them to be like a chess board.
Can anyone help me?
Create your own class MyGridView which extends GridView.
Override its onMeasure(...) method and set height == width, like so:
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
}
Then, create your own GridViewItem extending a View subclass e.g. TextView or ImageView etc. Override its onMeasure(...) method in the same way.
MyGridViewand its items will all appear square.
On your GridView, you can set the vertical spacing and the horizontal spacing that is between two items. Try setting those to 0dp.
<GridView
android:id="#+id/squareBoard"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:verticalSpacing="0dp"
android:horizontalSpacing="0dp"
android:numColumns="5"
/>
I'd like to get the exact, pixel position of the ListView scroll.
And no, I am not referring to the first visible position.
Is there a way to achieve this?
Okay, I found a workaround, using the following code:
View c = listview.getChildAt(0);
int scrolly = -c.getTop() + listview.getFirstVisiblePosition() * c.getHeight();
The way it works is it takes the actual offset of the first visible list item and calculates how far it is from the top of the view to determine how much we are "scrolled into" the view, so now that we know that we can calculate the rest using the regular getFirstVisiblePosition method.
Saarraz1's answer will only work if all the rows in the listview are of the same height and there's no header (or it is also the same height as the rows).
Note that once the rows disappear at the top of the screen you don't have access to them, as in you won't be able to keep track of their height. This is why you need to save those heights (or accumulated heights of all). My solution requires keeping a Dictionary of heights per index (it is assumed that when the list is displayed the first time it is scrolled to the top).
private Dictionary<Integer, Integer> listViewItemHeights = new Hashtable<Integer, Integer>();
private int getScroll() {
View c = listView.getChildAt(0); //this is the first visible row
int scrollY = -c.getTop();
listViewItemHeights.put(listView.getFirstVisiblePosition(), c.getHeight());
for (int i = 0; i < listView.getFirstVisiblePosition(); ++i) {
if (listViewItemHeights.get(i) != null) // (this is a sanity check)
scrollY += listViewItemHeights.get(i); //add all heights of the views that are gone
}
return scrollY;
}
Simplest idea I could come up with was to extend ListView and expose the "computeVerticalScrollOffset" which is protected by default, then use "com.your.package.CustomListView" in your xml layouts.
public class CustomListView extends ListView {
public CustomListView(Context context) {
super(context);
}
public CustomListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public int computeVerticalScrollOffset() {
return super.computeVerticalScrollOffset();
}
}
First Declare your int variable for hold the position.
int position = 0;
then add scrollListener to your ListView,
listView.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
position = firstVisibleItem;
}
});
Then after getting new data or any changes in your data that time you need to set the listview current position
listView.setSelection(position);
I have used after setup my adapter , works fine for me..
If anyone else found this in Google while looking for a way to track relative scroll offsets in an OnScrollListener - that is, change in Y since the last call to the listener - here's a Gist showing how to calculate that.
I know I'm late to the party but I felt like sharing my solution to this problem. I have a ListView and I was trying to find how much I have scrolled in order to scroll something else relative to it and cause a parallax effect. Here's my solution:
public abstract class OnScrollPositionChangedListener implements AbsListView.OnScrollListener {
int pos;
int prevIndex;
int prevViewPos;
int prevViewHeight;
#Override
public void onScroll(AbsListView v, int i, int vi, int n) {
try {
View currView = v.getChildAt(0);
int currViewPos = Math.round(currView.getTop());
int diffViewPos = prevViewPos - currViewPos;
int currViewHeight = currView.getHeight();
pos += diffViewPos;
if (i > prevIndex) {
pos += prevViewHeight;
} else if (i < prevIndex) {
pos -= currViewHeight;
}
prevIndex = i;
prevViewPos = currViewPos;
prevViewHeight = currViewHeight;
} catch (Exception e) {
e.printStackTrace();
} finally {
onScrollPositionChanged(pos);
}
}
#Override public void onScrollStateChanged(AbsListView absListView, int i) {}
public abstract void onScrollPositionChanged(int scrollYPosition);
}
I created my own OnScrollListener where the method onScrollPositionChanged will be called every time onScroll gets called. But this method will have access to the calculated value representing the amount that the ListView has been scrolled.
To use this class, you can setOnClickListener to a new OnScrollPositionChangedListener and override the onScrollPositionChanged method.
If you need to use the onScroll method for other stuff then you can override that too but you need to call super.onScroll to get onScrollPositionChanged working correctly.
myListView.setOnScrollListener(
new OnScrollPositionChangedListener() {
#Override
public void onScroll(AbsListView v, int i, int vi, int n) {
super.onScroll(v, i, vi, n);
//Do your onScroll stuff
}
#Override
public void onScrollPositionChanged(int scrollYPosition) {
//Enjoy having access to the amount the ListView has scrolled
}
}
);
in addition to #jaredpetker answer.
ListView is not holding all the items in its scroll, so u need to operate only "visible" part of list. When you scroll down top items are shifted out and pushed as new item views. Thats how convertedView is came from (it's not empty item to fill, it's shifted item that is out of "visible" part of list. So u need to know how many items was before visible part multiply them with ListItemHeight and add headerHeight, thes how you can get real absolute offset in scroll. If u got not header, position 0 will be listItem, so you can simplify absoluteY += pos*listItemHeight;
public class CustomListView extends ListView {
private int listItemHeight = 140;
private int headerHeight = 200;
public CustomListView(Context context) {
super(context);
}
public CustomListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public int computeVerticalScrollOffset() {
final int y = super.computeVerticalScrollOffset();
int absoluteY = y;
int pos = getFirstVisiblePosition();
if(pos > 0){
absoluteY += (pos-1)*listItemHeight+headerHeight;
}
//use absoluteY
return y;
}
I had faced the similar problem, That I wanted to place the Vertical Seekbar at current scrolled value of ListView. So I have my own solution like this.
First Create Class
public abstract class OnScrollPositionChangedListener implements AbsListView.OnScrollListener {
int pos;
public int viewHeight = 0;
public int listHeight = 0;
#Override
public void onScroll(AbsListView v, int i, int vi, int n) {
try {
if(viewHeight==0) {
viewHeight = v.getChildAt(0).getHeight();
listHeight = v.getHeight();
}
pos = viewHeight * i;
} catch (Exception e) {
e.printStackTrace();
} finally {
onScrollPositionChanged(pos);
}
}
#Override public void onScrollStateChanged(AbsListView absListView, int i) {}
public abstract void onScrollPositionChanged(int scrollYPosition);
}
Then use it in Main Activity like this.
public class MainActivity extends AppCompatActivity {
SeekBar seekBar;
ListView listView;
OnScrollPositionChangedListener onScrollPositionChangedListener = new OnScrollPositionChangedListener() {
#Override
public void onScroll(AbsListView v, int i, int vi, int n) {
super.onScroll(v, i, vi, n);
//Do your onScroll stuff
}
#Override
public void onScrollPositionChanged(int scrollYPosition) {
//Enjoy having access to the amount the ListView has scrolled
seekBar.setProgress(scrollYPosition);
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
seekBar = (SeekBar) findViewById(R.id.mySeekBar);
listView = (ListView) findViewById(R.id.listView);
final String[] values = new String[]{"Android List View",
"Adapter implementation",
"Simple List View In Android",
"Create List View Android",
"Android Example",
};
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, android.R.id.text1, values);
listView.setAdapter(adapter);
listView.setOnScrollListener(onScrollPositionChangedListener);
seekBar.postDelayed(new Runnable() {
#Override
public void run() {
seekBar.setMax((onScrollPositionChangedListener.viewHeight * values.length) - onScrollPositionChangedListener.listHeight);
}
}, 1000);
seekBar.setEnabled(false);
}
}
in App Gradle
compile 'com.h6ah4i.android.widget.verticalseekbar:verticalseekbar:0.7.0'
In XML Layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="com.centsol.charexamples.MainActivity">
<ListView
android:id="#+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fadeScrollbars="false"
android:scrollbars="none"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true">
</ListView>
<!-- This library requires pair of the VerticalSeekBar and VerticalSeekBarWrapper classes -->
<com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBarWrapper
android:layout_width="wrap_content"
android:layout_alignParentRight="true"
android:layout_height="match_parent">
<com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBar
android:id="#+id/mySeekBar"
android:layout_width="0dp"
android:progressDrawable="#drawable/progress"
android:thumb="#drawable/thumb"
android:layout_height="0dp"
android:splitTrack="false"
app:seekBarRotation="CW90" /> <!-- Rotation: CW90 or CW270 -->
</com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBarWrapper>
<View
android:layout_width="1dp"
android:visibility="visible"
android:layout_alignParentRight="true"
android:layout_marginTop="16dp"
android:layout_marginRight="2.5dp"
android:layout_marginBottom="16dp"
android:background="#android:color/black"
android:layout_height="match_parent" />
</RelativeLayout>
background xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<solid android:color="#android:color/transparent"/>
</shape>
fill xml
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<solid android:color="#android:color/transparent" />
</shape>
progress xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="#android:id/background"
android:drawable="#drawable/background"/>
<item android:id="#android:id/progress">
<clip android:drawable="#drawable/fill" />
</item>
</layer-list>
thumb xml
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval" >
<solid android:color="#android:color/holo_red_dark" />
<size
android:height="5dp"
android:width="5dp" />
</shape>