I want develope this rotation in my App . I have implemented the "down" menu ("můj dealer", "moje Volvo", "kontakty") and I need implement the "upper" rotating menu.
How can I do it? Do you have tips? Hope you understand me.
Images (please, watch the video above in the link):
Menu_item_test.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="#+id/menuItemImg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="#drawable/tab_1_1" />
</RelativeLayout>
Activity_main.xml:
<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"
tools:context=".MainActivity" >
<FrameLayout
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#+id/rotationMenu" >
</FrameLayout>
<ImageView
android:id="#+id/imageViewShadow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="60dp"
android:contentDescription="shadow"
android:scaleType="fitXY"
android:src="#drawable/half_round_back" />
<cz.mixedapps.volvista.RotationMenu
android:id="#+id/rotationMenu"
android:layout_width="match_parent"
android:layout_height="265dp"
android:layout_alignParentBottom="true" >
</cz.mixedapps.volvista.RotationMenu>
</RelativeLayout>
I have put together a custom view which replicates the behaviour, you still have to style it like in you example, but it works with an arbitrary number of child views and currently looks like this:
EDIT: I added the ability to show and hide the upper rotating menu with two methods, showRotationMenu() and hideRotationMenu():
Of course there is a lot you can do to improve this view. I just wrote this in 15 minutes and therefor it is a little rough around the edges. But it should be more than enough to put you on the right track. Both the looks of the buttons and of the views which rotate is completely customisable.
1) Source
RotationMenu.java:
import android.content.Context;
import android.content.res.Resources;
import android.database.DataSetObserver;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.*;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
/**
* Created by Xaver Kapeller on 26/03/14.
*/
public class RotationMenu extends LinearLayout {
private final DataSetObserver dataSetObserver = new DataSetObserver() {
#Override
public void onChanged() {
super.onChanged();
reloadAdapter();
}
};
private RotationMenuAdapter adapter;
private FrameLayout flViewContainer;
private LinearLayout llMenu;
private View currentView;
private View previousView;
private int animationPivotX;
private int animationPivotY;
private int selectedPosition = 0;
public RotationMenu(Context context) {
super(context);
setupMenu();
}
public RotationMenu(Context context, AttributeSet attrs) {
super(context, attrs);
setupMenu();
}
public RotationMenu(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setupMenu();
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
this.animationPivotX = w / 2;
this.animationPivotY = h;
}
private void setupMenu() {
this.setOrientation(VERTICAL);
this.flViewContainer = new FrameLayout(getContext());
this.addView(this.flViewContainer, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dpToPixel(150)));
this.llMenu = new LinearLayout(getContext());
this.llMenu.setOrientation(LinearLayout.HORIZONTAL);
this.addView(this.llMenu, new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
}
private int dpToPixel(int dp) {
float scale = getDisplayDensityFactor();
return (int) (dp * scale + 0.5f);
}
private float getDisplayDensityFactor() {
Resources res = getResources();
if (res != null) {
DisplayMetrics metrics = res.getDisplayMetrics();
if(metrics != null) {
return metrics.density;
}
}
return 1.0f;
}
public RotationMenuAdapter getAdapter() {
return this.adapter;
}
public void setAdapter(RotationMenuAdapter adapter) {
if (adapter != null) {
if (this.adapter != null) {
this.adapter.unregisterDataSetObserver(this.dataSetObserver);
}
adapter.registerDataSetObserver(this.dataSetObserver);
this.adapter = adapter;
reloadAdapter();
}
}
public boolean isRotationMenuVisible() {
return this.flViewContainer.getVisibility() == View.VISIBLE;
}
public void showRotationMenu() {
if(this.flViewContainer.getVisibility() != View.VISIBLE) {
this.flViewContainer.setVisibility(View.VISIBLE);
AnimationSet set = new AnimationSet(false);
TranslateAnimation translateAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 1, Animation.RELATIVE_TO_SELF, 0);
translateAnimation.setDuration(1000);
set.addAnimation(translateAnimation);
AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);
alphaAnimation.setDuration(1000);
set.addAnimation(alphaAnimation);
this.flViewContainer.startAnimation(set);
}
}
public void hideRotationMenu() {
if(this.flViewContainer.getVisibility() == View.VISIBLE) {
AnimationSet set = new AnimationSet(false);
TranslateAnimation translateAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 1);
translateAnimation.setDuration(1000);
set.addAnimation(translateAnimation);
AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0);
alphaAnimation.setDuration(1000);
set.addAnimation(alphaAnimation);
set.setAnimationListener(new ViewAnimationEndListener(this.flViewContainer) {
#Override
protected void onAnimationEnd(Animation animation, View view) {
view.setVisibility(View.GONE);
}
});
this.flViewContainer.startAnimation(set);
}
}
private void reloadAdapter() {
Context context = getContext();
if (this.adapter != null && context != null) {
int viewCount = this.adapter.getCount();
int oldViewCount = this.llMenu.getChildCount();
for (int i = 0; i < Math.max(oldViewCount, viewCount); i++) {
if (i < viewCount) {
LayoutParams layoutParams = new LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1);
View menuItem;
if (i < this.llMenu.getChildCount()) {
menuItem = this.adapter.getMenuItemView(i, this.llMenu.getChildAt(i), this.llMenu);
if(menuItem.getParent() == null) {
this.llMenu.removeViewAt(i);
this.llMenu.addView(menuItem, i, layoutParams);
}
} else {
menuItem = this.adapter.getMenuItemView(i, null, this.llMenu);
this.llMenu.addView(menuItem, layoutParams);
}
menuItem.setOnClickListener(new MenuItemClickListener(i));
} else {
this.llMenu.removeViewAt(i);
}
}
this.flViewContainer.removeAllViews();
this.previousView = this.currentView;
if (this.selectedPosition >= viewCount) {
this.selectedPosition = viewCount - 1;
}
this.currentView = this.adapter.getView(this.selectedPosition, this.previousView, this);
addViewWithAnimation(this.currentView, false);
}
}
public void switchToItem(int position, boolean animate) {
if (this.adapter != null) {
int viewCount = this.adapter.getCount();
position = valueInRange(position, 0, viewCount - 1);
if (position != this.selectedPosition) {
View oldView = this.currentView;
this.currentView = this.adapter.getView(position, this.previousView, this);
this.previousView = oldView;
addViewWithAnimation(this.currentView, animate, position < this.selectedPosition);
removeViewWithAnimation(this.previousView, animate, position < this.selectedPosition);
this.selectedPosition = position;
}
}
}
private int valueInRange(int value, int min, int max) {
if (value > max) {
value = max;
} else if (value < min) {
value = min;
}
return value;
}
private void addViewWithAnimation(View view, boolean animate, boolean leftToRight) {
if (view != null) {
if(view.getParent() == null) {
this.flViewContainer.addView(view, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
if (animate) {
int start = leftToRight ? -90 : 90;
Animation rotateIn = new RotateAnimation(start, 0, this.animationPivotX, this.animationPivotY);
rotateIn.setDuration(1000);
view.startAnimation(rotateIn);
}
}
}
private void addViewWithAnimation(View view, boolean animate) {
addViewWithAnimation(view, animate, true);
}
private void removeViewWithAnimation(View view, boolean animate, boolean leftToRight) {
if (view != null) {
if (animate) {
int target = leftToRight ? 90 : -90;
Animation rotateOut = new RotateAnimation(0, target, this.animationPivotX, this.animationPivotY);
rotateOut.setDuration(1000);
rotateOut.setAnimationListener(new ViewAnimationEndListener(view) {
#Override
protected void onAnimationEnd(Animation animation, View view) {
flViewContainer.removeView(view);
}
});
view.startAnimation(rotateOut);
} else {
this.flViewContainer.removeView(view);
}
}
}
private void removeViewWithAnimation(View view, boolean animate) {
removeViewWithAnimation(view, animate, true);
}
private abstract class ViewAnimationEndListener implements Animation.AnimationListener {
private final View view;
private ViewAnimationEndListener(View view) {
this.view = view;
}
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
onAnimationEnd(animation, this.view);
}
#Override
public void onAnimationRepeat(Animation animation) {
}
protected abstract void onAnimationEnd(Animation animation, View view);
}
private class MenuItemClickListener implements OnClickListener {
private final int position;
MenuItemClickListener(int position) {
this.position = position;
}
#Override
public void onClick(View v) {
if(adapter != null && adapter.isMenuItemEnabled(this.position) && isRotationMenuVisible()) {
switchToItem(this.position, true);
}
}
}
}
RotationMenuAdapter.java:
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
/**
* Created by Xaver Kapeller on 26/03/14.
*/
public abstract class RotationMenuAdapter extends BaseAdapter {
public abstract View getMenuItemView(int position, View convertView, ViewGroup parent);
public abstract long getMenuItemId(int position);
public abstract boolean isMenuItemEnabled(int position);
}
2) Usage:
The custom view is making use of adapters and view recycling, so you use it the same way you would use a ListView. Because of the view recycling it is not very memory intensive and actually pretty fast. The logic for the creating of the menu items at the bottom is also in the adapter and it too uses view recycling. So be sure that you implement getMenuItemView() with the same care you would use to implement getView(). First you have to write an adapter:
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import at.test.app.R;
import at.test.app.RotationMenu.RotationMenuAdapter;
import java.util.List;
/**
* Created by Xaver Kapeller on 26/03/14.
*/
public class TestAdapter extends RotationMenuAdapter {
private static final long TEST_VIEW_ID = 0;
private static final long DEFAULT_VIEW_ID = TEST_VIEW_ID;
private static final long TEST_MENU_ID = 0;
private static final long DEFAULT_MENU_ID = TEST_MENU_ID;
private final LayoutInflater inflater;
private final List<TestViewModel> viewModels;
public TestAdapter(Context context, List<TestViewModel> viewModels) {
this.inflater = LayoutInflater.from(context);
this.viewModels = viewModels;
}
#Override
public int getCount() {
return this.viewModels.size();
}
#Override
public Object getItem(int position) {
return this.viewModels.get(position);
}
#Override
public long getItemId(int position) {
Object viewModel = getItem(position);
if(viewModel instanceof TestViewModel) {
return TEST_VIEW_ID;
}
return DEFAULT_VIEW_ID;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if(getItemId(position) == TEST_VIEW_ID) {
TestViewModel viewModel = (TestViewModel) getItem(position);
TestRow row;
if(convertView == null) {
convertView = this.inflater.inflate(TestRow.LAYOUT, parent, false);
row = new TestRow(convertView);
convertView.setTag(row);
}
row = (TestRow) convertView.getTag();
row.bind(viewModel);
}
return convertView;
}
#Override
public View getMenuItemView(int position, View convertView, ViewGroup parent) {
if(getMenuItemId(position) == TEST_MENU_ID) {
TestViewModel viewModel = (TestViewModel)getItem(position);
MenuRow row;
if(convertView == null) {
convertView = this.inflater.inflate(MenuRow.LAYOUT, parent, false);
row = new MenuRow(convertView);
convertView.setTag(row);
}
row = (MenuRow)convertView.getTag();
row.bind(viewModel);
}
return convertView;
}
#Override
public long getMenuItemId(int position) {
Object item = getItem(position);
if(item instanceof TestViewModel) {
return TEST_MENU_ID;
}
return DEFAULT_MENU_ID;
}
#Override
public boolean isMenuItemEnabled(int position) {
return true;
}
}
Nothing special, except the extra methods for the creating of the menu items. In isMenuItemEnabled() you can add logic to enable/disable menu items if you need to.
And then you apply your adapter to the view:
List<TestViewModel> viewModels = new ArrayList<TestViewModel>();
TestViewModel menuItemOne = new TestViewModel();
menuItemOne.setMenuItemIconResId(R.drawable.icon);
viewModels.add(menuItemOne);
TestViewModel menuItemTwo = new TestViewModel();
menuItemTwo.setMenuItemIconResId(R.drawable.icon);
viewModels.add(menuItemTwo);
TestAdapter adapter = new TestAdapter(getActivity(), viewModels);
this.rotationMenu.setAdapter(adapter);
EDIT:
Try this layout with wrap_content instead match_parent:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<ImageView
android:id="#+id/menuItemImg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="fitXY"
android:src="#drawable/tab_1_1" />
</RelativeLayout>
I'm also no so sure about your layout_width, are you sure you need match_parent there? I don't think so.
Related
I have a ListView, inside a linear layout, that should show items based on a list_item.xml layout file. In the layout file I have two ImageViews and two TextViews. I'm using my custom ArrayAdapter to fill the ListView at runtime with Bitmap images of scale 125x25, however images are showing smaller than their initial size when rendered by the ImageView inside the ListView as shown in the screenshot (of a single list item) below:
ActivityLayout.xml:
...
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:paddingTop="40dp"
android:orientation="vertical">
<ListView
android:id="#+id/lvExp"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
...
List_item.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:minHeight="?android:attr/listPreferredItemHeight">
<ImageView
android:id="#+id/imageViewplate"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:adjustViewBounds="true"
android:minWidth="300dp" />
<TextView
android:id="#+id/lblListItem_letter"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:layout_toRightOf="#id/imageViewplate"
android:padding="3dp"
android:text="A"
android:textSize="17dip" />
<TextView
android:id="#+id/lblListItem_num"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:layout_toRightOf="#id/lblListItem_letter"
android:padding="3dp"
android:text="123456"
android:textSize="17dip" />
<ImageView
android:id="#+id/imageViewchars"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_marginLeft="10dp"
android:paddingTop="3dp"
android:paddingBottom="3dp" />
</RelativeLayout>
My custom list adapter:
package bi.anpr.layouts;
import android.content.Context;
import android.support.design.widget.Snackbar;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.sxx.vlctest.R;
import java.util.ArrayList;
import bi.anpr.agents.Plate;
public class PlateListAdapter extends ArrayAdapter<Plate>{
public PlateListAdapter(Context context, int resource) {
super(context, resource);
}
private ArrayList<Plate> dataSet;
Context mContext;
private static class ViewHolder {
public ImageView imageView_plate, imageView_chars;
public TextView textView_letter, textView_numbers;
}
private int lastPosition = -1;
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
Plate plate = getItem(position);
ViewHolder viewHolder;
final View result;
if (convertView == null) {
viewHolder = new ViewHolder();
LayoutInflater inflater = LayoutInflater.from(getContext());
convertView = inflater.inflate(R.layout.list_item, parent, false);
viewHolder.textView_letter = (TextView) convertView.findViewById(R.id.lblListItem_letter);
viewHolder.textView_numbers = (TextView) convertView.findViewById(R.id.lblListItem_num);
viewHolder.imageView_plate = (ImageView) convertView.findViewById(R.id.imageViewplate);
viewHolder.imageView_chars = (ImageView) convertView.findViewById(R.id.imageViewchars);
result = convertView;
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
result = convertView;
}
lastPosition = position;
try{
viewHolder.imageView_plate.setImageBitmap(plate.getEngPartImageIcon());
viewHolder.imageView_chars.setImageBitmap(plate.getcharsImageIcon());
viewHolder.textView_letter.setText(Character.toString(plate.getPlateLetter()));
viewHolder.textView_numbers.setText(plate.getPlateNumbersAsString());
}catch (Exception e){
e.printStackTrace();
}
// Return the completed view to render on screen
return convertView;
}
}
Plate.java:
package bi.anpr.agents;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import java.io.Serializable;
import java.util.ArrayList;
import org.opencv.core.Mat;
import org.opencv.core.Rect;
import bi.anpr.core.IPHelper;
public class Plate implements Serializable {
private String platePath;
private boolean isWidePlate = true;
private int[] color;
private String colorString;
private String source = "";
private Rect bdgbx;
private Mat srcImage;
private Mat plateImage;
private Bitmap engPartImageIcon, plateImageIcon, forCutImageIcon, charsImageIcon, charsTextImageIcon;
private Mat engPartImage;
private Mat forCut;
private ArrayList<Character> plateNumberChars = new ArrayList<>();
private char plateLetterChar = '#';
private String fullPlateString = "";
private String numbersPlateString = "";
private double letterRate = 0;
private double numbersRate = 0;
private double accuracy = 0;
private int id;
private PlateRecogInfo plateRecogInfo;
private ArrayList<Bitmap> charsIcons = new ArrayList<>();
private boolean ignore = false;
private int accurayPercent = 0;
public Plate() {
plateRecogInfo = new PlateRecogInfo();
}
public void addPlateNumberChar(char c) {
plateNumberChars.add(c);
}
public Bitmap getplateImageIcon() {
return plateImageIcon;
}
public void setPlateImageIcon(Bitmap plateImageIcon) {
this.plateImageIcon = plateImageIcon;
}
public Bitmap getEngPartImageIcon() {
if (engPartImageIcon == null) {
engPartImageIcon = IPHelper.matToBitmap(engPartImage);
}
return engPartImageIcon;
}
public void setEngPartImageIcon(Bitmap engPartImageIcon) {
this.engPartImageIcon = engPartImageIcon;
}
public Mat getEngPartImage() {
return engPartImage;
}
public void setEngPartImage(Mat engPartImage) {
this.engPartImage = engPartImage;
}
public boolean isWidePlate() {
return isWidePlate;
}
public void setWidePlate(boolean isWidePlate) {
this.isWidePlate = isWidePlate;
}
public Mat getImage() {
return plateImage;
}
public void setImage(Mat Bitmap) {
plateImage = Bitmap;
}
public Bitmap getForCutImageIcon() {
if (forCutImageIcon == null) {
forCutImageIcon = IPHelper.matToBitmap(forCut);
}
return forCutImageIcon;
}
public Mat getForCut() {
return forCut;
}
public void setForCutImageIcon(Bitmap forCutImageIcon) {
this.forCutImageIcon = forCutImageIcon;
}
public void setForCut(Mat forCut) {
this.forCut = forCut;
}
public ArrayList<Character> getPlateNumberChar() {
return plateNumberChars;
}
public void setPlateNumberChar(ArrayList<Character> nchar) {
plateNumberChars = new ArrayList<Character>(nchar);
}
public char getPlateLetter() {
return plateLetterChar;
}
public void setPlateLetter(char plateLetter) {
plateLetterChar = plateLetter;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public double getLetterRate() {
return letterRate;
}
public void setLetterRate(double letterRate) {
this.letterRate = letterRate;
}
public double getCharsRate() {
return numbersRate;
}
public void setNumbersRate(double NumbersRate) {
this.numbersRate = NumbersRate;
}
public double getAccuracy() {
return accuracy;
}
public int getAccurayPercent() {
return accurayPercent;
}
public void setAccuracy(double totalRate) {
this.accuracy = totalRate;
this.accurayPercent = (int) (totalRate * 100);
}
public Mat getPlateImage() {
return plateImage;
}
public void setPlateImage(Mat matImage) {
this.plateImage = matImage;
plateImageIcon = IPHelper.matToBitmap(matImage);
}
public PlateRecogInfo getPlateRecogInfo() {
return plateRecogInfo;
}
public void setPlateRecogInfo(PlateRecogInfo plateRecogInfo) {
this.plateRecogInfo = plateRecogInfo;
}
public void addCharIcon(Mat charsIcons) {
this.charsIcons.add(IPHelper.matToBitmap(charsIcons));
}
public Bitmap getcharsImageIcon() {
if (charsImageIcon == null) {
int width = 5;
int height = 0;
for (Bitmap icon : charsIcons) {
if (icon.getHeight() > height)
height = icon.getHeight();
width += icon.getWidth() + 5;
}
height += 6;
charsImageIcon = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(charsImageIcon);
int i = 0;
for (Bitmap icon : charsIcons) {
canvas.drawBitmap(icon, i, 3, null);
i += icon.getWidth() + 5;
}
}
return charsImageIcon;
}
public Bitmap getCharsTextImageIcon() {
if (charsTextImageIcon == null) {
String chars = "";
for (Character c : plateNumberChars) {
String cs = c.toString();
chars += cs;
}
int width = 5;
int height = 0;
for (Bitmap icon : charsIcons) {
if (icon.getHeight() > height)
height = icon.getHeight();
width += icon.getWidth() + 5;
}
height += 6;
charsTextImageIcon = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(charsTextImageIcon);
int i = 0;
int j = 0;
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
paint.setColor(Color.BLACK);
paint.setTextSize(16);
canvas.drawColor(Color.WHITE);
for (Bitmap icon : charsIcons) {
if (j == 0)
canvas.drawText("Some Text", i + 2, 19, paint);
else
canvas.drawText(Character.toString(chars.charAt(j - 1)), i + 3, 19, paint);
i += icon.getWidth() + 5;
j++;
}
}
return charsTextImageIcon;
}
public void setCharsTextImageIcon(Bitmap charsTextImageIcon) {
this.charsTextImageIcon = charsTextImageIcon;
}
public Mat getSrcImage() {
return srcImage;
}
public void setSrcImage(Mat srcImage) {
this.srcImage = srcImage;
}
public Rect getBdgbx() {
return bdgbx;
}
public void setBdgbx(Rect bdgbx) {
this.bdgbx = bdgbx;
}
public int[] getColor() {
return color;
}
// public Color getColorObject() {
// int[] color = getColor();
//
// try {
// return Color.parseColor(Color.rgb(color[0], color[1], color[2]));
// } catch (Exception e) {
// }
// return null;
// }
public void setColor(int[] is) {
this.color = is;
}
public String getColorString() {
return colorString;
}
public void setColorString(String colorString) {
this.colorString = colorString;
}
public String getFullPlateCharsString() {
if (fullPlateString == "" || fullPlateString == null) {
fullPlateString += plateLetterChar;
for (Character c : plateNumberChars) {
fullPlateString += c.toString();
}
}
return fullPlateString;
}
public void setFullPlateString(String fullPlateString) {
this.fullPlateString = fullPlateString;
}
public void setNumbersPlateString(String numbersPslateString) {
this.numbersPlateString = numbersPslateString;
}
public String getPlateNumbersAsString() {
if (numbersPlateString == "" || numbersPlateString == null) {
for (Character c : plateNumberChars) {
numbersPlateString += c.toString();
}
}
return numbersPlateString;
}
public String getPlatePath() {
return platePath;
}
public void setPlatePath(String platePath) {
this.platePath = platePath;
}
public boolean isIgnore() {
return ignore;
}
public void setIgnore(boolean ignore) {
this.ignore = ignore;
}
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
}
Usage:
PlateListAdapter plateListAdapter = new PlateListAdapter(getApplicationContext(), R.layout.list_item);
ListView resultsListView = (ListView) findViewById(R.id.lvExp);
resultsListView.setAdapter(plateListAdapter);
plateListAdapter.addAll(recogInfo.getPlates());
plateListAdapter.notifyDataSetChanged();
Using definite dimensions for ImageView's, rather than wrap_content solved the problem.
This is what I want:
As image above, I want to draw a center line on RecycleView, then get the center item when scrolling (as well as move left or right)
Here is my try to draw a horizontal RecycleView:
HorizontalAdapter adapter = new HorizontalAdapter(data);
LinearLayoutManager layoutManager
= new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
recycleView.setLayoutManager(layoutManager);
recycleView.setAdapter(adapter);
Is there any way to know which item is moved to the center of RecycleView? And how can I scroll RecycleView to left or right just one position?
Update: I tried to use a scroll listener to get the middle position, but it doesn't work as an aspect.
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int firstPos = layoutManager.findFirstVisibleItemPosition();
int lastPos = layoutManager.findLastVisibleItemPosition();
int middle = Math.abs(lastPos - firstPos) / 2 + firstPos;
int selectedPos = -1;
for (int i = 0; i < adapter.getItemCount(); i++) {
if (i == middle) {
adapter.getItem(i).setSelected(true);
selectedPos = i;
} else {
adapter.getItem(i).setSelected(false);
}
}
adapter.notifyDataSetChanged();
}
And get the result:
I only want to change the selected item (make text to white color) when it is on the blue Rect
I made something just like this. I can do exactly what you need.
First of all, this is how is my alogrithm work
This is my recyclerView Adapter
public class DateAdapter extends RecyclerView.Adapter<DateAdapter.DateViewHolder> {
private ArrayList<LabelerDate> dateDataList;
private static final int VIEW_TYPE_PADDING = 1;
private static final int VIEW_TYPE_ITEM = 2;
private int paddingWidthDate = 0;
private int selectedItem = -1;
public DateAdapter(ArrayList<LabelerDate> dateData, int paddingWidthDate) {
this.dateDataList = dateData;
this.paddingWidthDate = paddingWidthDate;
}
#Override
public DateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_ITEM) {
final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_date,
parent, false);
return new DateViewHolder(view);
} else {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_padding,
parent, false);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
layoutParams.width = paddingWidthDate;
view.setLayoutParams(layoutParams);
return new DateViewHolder(view);
}
}
#Override
public void onBindViewHolder(DateViewHolder holder, int position) {
LabelerDate labelerDate = dateDataList.get(position);
if (getItemViewType(position) == VIEW_TYPE_ITEM) {
if(labelerDate.dateType.equals(BirthDayActivity.DateType.C31))
holder.tvDate.setText(String.valueOf(labelerDate.valueDate));
holder.tvDate.setVisibility(View.VISIBLE);
holder.imgSmall.setVisibility(View.VISIBLE);
if (position == selectedItem) {
holder.tvDate.setTextColor(Color.parseColor("#094673"));
holder.tvDate.setTextSize(35);
holder.imgSmall.setBackgroundResource(R.color.textviewbold);
} else {
holder.tvDate.setTextColor(Color.GRAY);
holder.tvDate.setTextSize(35);
holder.imgSmall.setBackgroundResource(R.color.gray);
}
}
}
public void setSelecteditem(int selecteditem) {
this.selectedItem = selecteditem;
notifyDataSetChanged();
}
#Override
public int getItemCount() {
return dateDataList.size();
}
#Override
public int getItemViewType(int position) {
LabelerDate labelerDate = dateDataList.get(position);
if (labelerDate.dateType.equals(BirthDayActivity.DateType.NONE)) {
return VIEW_TYPE_PADDING;
}
return VIEW_TYPE_ITEM;
}
public class DateViewHolder extends RecyclerView.ViewHolder {
public TextView tvDate;
public ImageView imgSmall;
public DateViewHolder(View itemView) {
super(itemView);
tvDate = (TextView) itemView.findViewById(R.id.tvNumberDate);
imgSmall = (ImageView) itemView.findViewById(R.id.small_marked_dob);
}
}}
This is most important alogrithm:
public void getRecyclerviewDate() {
recyclerViewDate = (RecyclerView) findViewById(R.id.recyclerViewDay);
ViewTreeObserver vtoDate = recyclerViewDate.getViewTreeObserver();
vtoDate.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
recyclerViewDate.getViewTreeObserver().removeOnPreDrawListener(this);
finalWidthDate = recyclerViewDate.getMeasuredWidth();
itemWidthDate = getResources().getDimension(R.dimen.item_dob_width);
paddingDate = (finalWidthDate - itemWidthDate) / 2;
firstItemWidthDate = paddingDate ;
allPixelsDate = 0;
final LinearLayoutManager dateLayoutManager = new LinearLayoutManager(getApplicationContext());
dateLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerViewDate.setLayoutManager(dateLayoutManager);
recyclerViewDate.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
synchronized (this) {
if(newState == RecyclerView.SCROLL_STATE_IDLE){
calculatePositionAndScrollDate(recyclerView);
}
}
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
allPixelsDate += dx;
}
});
if (labelerDates == null)
labelerDates = new ArrayList<>();
labelerDates.addAll(genLabelerDate(currentMonth, currentYear));
dateAdapter = new DateAdapter(labelerDates, (int) firstItemWidthDate);
recyclerViewDate.setAdapter(dateAdapter);
return true;
}
});
}
/* this if most important, if expectedPositionDate < 0 recyclerView will return to nearest item*/
private void calculatePositionAndScrollDate(RecyclerView recyclerView) {
int expectedPositionDate = Math.round((allPixelsDate + paddingDate - firstItemWidthDate) / itemWidthDate);
if (expectedPositionDate == -1) {
expectedPositionDate = 0;
} else if (expectedPositionDate >= recyclerView.getAdapter().getItemCount() - 2) {
expectedPositionDate--;
}
scrollListToPositionDate(recyclerView, expectedPositionDate);
}
/* this if most important, if expectedPositionDate < 0 recyclerView will return to nearest item*/
private void scrollListToPositionDate(RecyclerView recyclerView, int expectedPositionDate) {
float targetScrollPosDate = expectedPositionDate * itemWidthDate + firstItemWidthDate - paddingDate;
float missingPxDate = targetScrollPosDate - allPixelsDate;
if (missingPxDate != 0) {
recyclerView.smoothScrollBy((int) missingPxDate, 0);
}
}
private void setDateValue() {
int expectedPositionDateColor = Math.round((allPixelsDate + paddingDate - firstItemWidthDate) / itemWidthDate);
setColorDate = expectedPositionDateColor + 1;
//set color here
dateAdapter.setSelecteditem(setColorDate);
}
#Override
protected void onRestoreInstanceState(#NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
allPixelsDate = savedInstanceState.getFloat(BUNDLE_LIST_PIXELS_DATE);
allPixelsDateChanged = savedInstanceState.getFloat(BUNDLE_LIST_PIXELS_DATE_CHANGED);
}
#Override
protected void onSaveInstanceState(#NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putFloat(BUNDLE_LIST_PIXELS_DATE, allPixelsDate);
outState.putFloat(BUNDLE_LIST_PIXELS_DATE_CHANGED, allPixelsDateChanged);
}
And this is my result:
Look at this video link, this is my app demo
Sometimes is needed the entire example code block together, because we may miss something. Here is what I have, feel free to correct anything since I may be doing some little mistake somewhere. And Yes, this answer is an extension of #tranhieu answer. Thanks #tranhieu.
MainActivity.java
package com.test;
import android.app.Activity;
import android.graphics.Color;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.TextView;
import java.util.ArrayList;
public class MainActivity extends Activity {
private static final String TAG = MainActivity.class.getSimpleName();
public float firstItemWidthDate;
public float paddingDate;
public float itemWidthDate;
public int allPixelsDate;
public int finalWidthDate;
private DateAdapter dateAdapter;
private ArrayList<LabelerDate> labelerDates = new ArrayList<>();
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getRecyclerviewDate();
}
public void getRecyclerviewDate() {
final RecyclerView recyclerViewDate = (RecyclerView) findViewById(R.id.rv_tasks_date);
if (recyclerViewDate != null) {
recyclerViewDate.postDelayed(new Runnable() {
#Override
public void run() {
setDateValue();
}
}, 300);
recyclerViewDate.postDelayed(new Runnable() {
#Override
public void run() {
recyclerViewDate.smoothScrollToPosition(dateAdapter.getItemCount()-1);
setDateValue();
}
}, 5000);
}
ViewTreeObserver vtoDate = recyclerViewDate.getViewTreeObserver();
vtoDate.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
recyclerViewDate.getViewTreeObserver().removeOnPreDrawListener(this);
finalWidthDate = recyclerViewDate.getMeasuredWidth();
itemWidthDate = getResources().getDimension(R.dimen.item_dob_width);
paddingDate = (finalWidthDate - itemWidthDate) / 2;
firstItemWidthDate = paddingDate;
allPixelsDate = 0;
final LinearLayoutManager dateLayoutManager = new LinearLayoutManager(getApplicationContext());
dateLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerViewDate.setLayoutManager(dateLayoutManager);
recyclerViewDate.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
synchronized (this) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
calculatePositionAndScrollDate(recyclerView);
}
}
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
allPixelsDate += dx;
}
});
if (labelerDates == null) {
labelerDates = new ArrayList<>();
}
genLabelerDate();
dateAdapter = new DateAdapter(labelerDates, (int) firstItemWidthDate);
recyclerViewDate.setAdapter(dateAdapter);
dateAdapter.setSelecteditem(dateAdapter.getItemCount() - 1);
return true;
}
});
}
private void genLabelerDate() {
for (int i = 0; i < 32; i++) {
LabelerDate labelerDate = new LabelerDate();
labelerDate.setNumber(Integer.toString(i));
labelerDates.add(labelerDate);
if (i == 0 || i == 31) {
labelerDate.setType(DateAdapter.VIEW_TYPE_PADDING);
} else {
labelerDate.setType(DateAdapter.VIEW_TYPE_ITEM);
}
}
}
/* this if most important, if expectedPositionDate < 0 recyclerView will return to nearest item*/
private void calculatePositionAndScrollDate(RecyclerView recyclerView) {
int expectedPositionDate = Math.round((allPixelsDate + paddingDate - firstItemWidthDate) / itemWidthDate);
if (expectedPositionDate == -1) {
expectedPositionDate = 0;
} else if (expectedPositionDate >= recyclerView.getAdapter().getItemCount() - 2) {
expectedPositionDate--;
}
scrollListToPositionDate(recyclerView, expectedPositionDate);
}
/* this if most important, if expectedPositionDate < 0 recyclerView will return to nearest item*/
private void scrollListToPositionDate(RecyclerView recyclerView, int expectedPositionDate) {
float targetScrollPosDate = expectedPositionDate * itemWidthDate + firstItemWidthDate - paddingDate;
float missingPxDate = targetScrollPosDate - allPixelsDate;
if (missingPxDate != 0) {
recyclerView.smoothScrollBy((int) missingPxDate, 0);
}
setDateValue();
}
//
private void setDateValue() {
int expectedPositionDateColor = Math.round((allPixelsDate + paddingDate - firstItemWidthDate) / itemWidthDate);
int setColorDate = expectedPositionDateColor + 1;
// set color here
dateAdapter.setSelecteditem(setColorDate);
}
public class DateAdapter extends RecyclerView.Adapter<DateAdapter.DateViewHolder> {
private ArrayList<LabelerDate> dateDataList;
private static final int VIEW_TYPE_PADDING = 1;
private static final int VIEW_TYPE_ITEM = 2;
private int paddingWidthDate = 0;
private int selectedItem = -1;
public DateAdapter(ArrayList<LabelerDate> dateData, int paddingWidthDate) {
this.dateDataList = dateData;
this.paddingWidthDate = paddingWidthDate;
}
#Override
public DateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_ITEM) {
final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item,
parent, false);
return new DateViewHolder(view);
} else {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item,
parent, false);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
layoutParams.width = paddingWidthDate;
view.setLayoutParams(layoutParams);
return new DateViewHolder(view);
}
}
#Override
public void onBindViewHolder(DateViewHolder holder, int position) {
LabelerDate labelerDate = dateDataList.get(position);
if (getItemViewType(position) == VIEW_TYPE_ITEM) {
holder.tvDate.setText(labelerDate.getNumber());
holder.tvDate.setVisibility(View.VISIBLE);
Log.d(TAG, "default " + position + ", selected " + selectedItem);
if (position == selectedItem) {
Log.d(TAG, "center" + position);
holder.tvDate.setTextColor(Color.parseColor("#76FF03"));
holder.tvDate.setTextSize(35);
} else {
holder.tvDate.setTextColor(Color.WHITE);
holder.tvDate.setTextSize(18);
}
} else {
holder.tvDate.setVisibility(View.INVISIBLE);
}
}
public void setSelecteditem(int selecteditem) {
this.selectedItem = selecteditem;
notifyDataSetChanged();
}
#Override
public int getItemCount() {
return dateDataList.size();
}
#Override
public int getItemViewType(int position) {
LabelerDate labelerDate = dateDataList.get(position);
if (labelerDate.getType() == VIEW_TYPE_PADDING) {
return VIEW_TYPE_PADDING;
} else {
return VIEW_TYPE_ITEM;
}
}
public class DateViewHolder extends RecyclerView.ViewHolder {
public TextView tvDate;
public DateViewHolder(View itemView) {
super(itemView);
tvDate = (TextView) itemView.findViewById(R.id.txt_date);
}
}
}
private class LabelerDate {
private int type;
private String number;
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView
android:id="#+id/rv_tasks_date"
android:layout_width="match_parent"
android:layout_height="48dp" />
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:layout_marginTop="48dp"
android:src="#android:drawable/ic_dialog_info" />
</FrameLayout>
</LinearLayout>
item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="wrap_content"
android:layout_height="match_parent">
<TextView
android:id="#+id/txt_date"
android:layout_width="#dimen/item_dob_width"
android:layout_height="48dp"
android:text="32"
android:textColor="#android:color/white"
android:background="#android:color/darker_gray"
android:textSize="28sp"
android:gravity="center"/>
</LinearLayout>
dimens.xml
<resources>
<dimen name="item_dob_width">100dp</dimen>
</resources>
Oh boy. I've been searching for this answer for almost a week and then found out the solution. Custom LayoutManagers? No. ItemDecorator? Nope.
Here's the easiest way to do it:
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:paddingStart="150dp"
android:paddingEnd="150dp"
android:clipToPadding="false" />
The critical part is:
android:paddingStart="150dp"
android:paddingEnd="150dp"
android:clipToPadding="false"
And then just assign SnapHelper to your RecylcerView:
val snapHelper = LinearSnapHelper()
snapHelper.attachToRecyclerView(recyclerView)
This is it. The easiest and most perfect solution to the problem
I'm used the SnapHelper right here:
// init snaphelper
SnapHelper snapHelper = new LinearSnapHelper();
snapHelper.attachToRecyclerView(recyclerView)
// init layout manager
LinearLayoutManager layoutManager = new LinearLayoutManager(mainActivity);
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.setLayoutManager(layoutManager);
// init adapter
adatper.setSnapHelper(snapHelper);
adatper.setLayoutManager(layoutManager);
adatper.initAdapter(new Float((DisplayHelper.getDisplayWidth(mainActivity) / 2) - (fooViewWidth / 2)).intValue());
recyclerView.setAdapter(adatper);
As said by TranHieu the solution of inserting 2 item for padding (at start and at end positions) is good.
I don't like the use of ViewTreeObserver because of poor readability of code. With this technique then you must also manage redrawing of the items if they are recycled.
If you are using customview classes you can set its width directly into these classes.
For example this is my padding class
/**
* Created by firegloves on 25/09/15.
*/
#EViewGroup(R.layout.view_padding)
public class PaddingView extends FooView {
Context mCtx;
public PaddingView(Context context) {
super(context);
mCtx = context;
}
public void setWidth(int width) {
setLayoutParams(new LayoutParams(width, ViewGroup.LayoutParams.WRAP_CONTENT));
}
}
In my adapter I store the desired padding item width, that is equal to (displayWidth / 2) - (realItemWidth / 2)
This is my adapter, don't look at methods not matching RecyclerView.Adapter, pay attention to the initAdapter method and to the onCreateItemView method
#EBean
public class FooAdapterRecycler extends RecyclerViewAdapterBase<Foo, FooView> {
private final int TYPE_PADDING_VIEW = 0;
private final int TYPE_REAL_VIEW = 1;
#RootContext
Context ctx;
#Bean(Finder.class)
IFinder finder;
SnapHelper snapHelper;
RecyclerView.LayoutManager layoutManager;
private int paddingWidth = 0;
/**
* preleva i dati dal finder
*/
public void initAdapter(int paddingWidth) {
/*******************************
* THIS CODE IS THE IMPORTANT ONE
******************************/
this.paddingWidth = paddingWidth;
// add 1 item for initial space
mItems = new ArrayList<>();
Foo foo = new Foo();
mItems.add(foo);
// get real items from finder
mItems.addAll(finder.findAll());
// add 1 item for final space
mItems = new ArrayList<>();
Foo foo2 = new Foo();
mItems.add(foo2);
}
#Override
public int getItemViewType(int position) {
if (position == 0 || position == getItemCount()-1) {
return TYPE_PADDING_VIEW;
} else {
return TYPE_REAL_VIEW;
}
}
#Override
protected FooView onCreateItemView(ViewGroup parent, int viewType) {
/*******************************
* THIS CODE IS THE IMPORTANT ONE
******************************/
if (viewType == TYPE_PADDING_VIEW) {
PaddingView view = PaddingView_.build(ctx);
view.setWidth(paddingWidth);
return view;
} else {
return FooView_.build(ctx);
}
}
public void setSnapHelper(SnapHelper snapHelper) {
this.snapHelper = snapHelper;
}
public void setLayoutManager(RecyclerView.LayoutManager layoutManager) {
this.layoutManager = layoutManager;
}
}
I'm using AndroidAnnotations library but it's not required
Hope that helps
USING SNAPHELPER - A SMOOTHER SOLUTION
Here it is another solution using SnapHelper. Starting from the answer of #TranHieu here:
https://stackoverflow.com/a/34647005/3944251
and the compressed by #sector11 here:
https://stackoverflow.com/a/38411582/3944251
I wrote the following code which is also based in both answers above, but it's simpler and offers a smoother solution using SnapHelper presented in android support library 24.2.0.
Here you have the MainActivity class. The rest is the same with #sector11's answer.
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.LinearSnapHelper;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.TextView;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
public float firstItemWidthDate;
public float itemWidthDate;
public int allPixelsDate;
public int finalWidthDate;
private DateAdapter dateAdapter;
private ArrayList<LabelerDate> labelerDates;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
labelerDates = new ArrayList<>();
getRecyclerviewDate();
}
public void getRecyclerviewDate() {
final RecyclerView recyclerViewDate = (RecyclerView) findViewById(R.id.rv_tasks_date);
recyclerViewDate.postDelayed(new Runnable() {
#Override
public void run() {
//recyclerViewDate.smoothScrollToPosition(dateAdapter.getItemCount()-1);
setDateValue();
}
}, 300);
ViewTreeObserver vtoDate = recyclerViewDate.getViewTreeObserver();
vtoDate.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
recyclerViewDate.getViewTreeObserver().removeOnPreDrawListener(this);
finalWidthDate = recyclerViewDate.getMeasuredWidth();
itemWidthDate = getResources().getDimension(R.dimen.item_dob_width);
firstItemWidthDate = (finalWidthDate - itemWidthDate) / 2;
allPixelsDate = 0;
final LinearLayoutManager dateLayoutManager = new LinearLayoutManager(getApplicationContext());
dateLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerViewDate.setLayoutManager(dateLayoutManager);
/* Create a LinearSnapHelper and attach the recyclerView to it. */
final LinearSnapHelper snapHelper = new LinearSnapHelper();
snapHelper.attachToRecyclerView(recyclerViewDate);
recyclerViewDate.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
allPixelsDate += dx;
recyclerView.post(new Runnable() {
public void run() {
setDateValue();
}
});
}
});
genLabelerDate();
dateAdapter = new DateAdapter(labelerDates, (int) firstItemWidthDate);
recyclerViewDate.setAdapter(dateAdapter);
dateAdapter.setSelecteditem(dateAdapter.getItemCount() - 1);
return true;
}
});
}
private void genLabelerDate() {
for (int i = 0; i < 32; i++) {
LabelerDate labelerDate = new LabelerDate();
labelerDate.setNumber(Integer.toString(i));
labelerDates.add(labelerDate);
if (i == 0 || i == 31) {
labelerDate.setType(DateAdapter.VIEW_TYPE_PADDING);
} else {
labelerDate.setType(DateAdapter.VIEW_TYPE_ITEM);
}
}
}
//
private void setDateValue() {
int expectedPositionDateColor = Math.round(allPixelsDate / itemWidthDate);
int setColorDate = expectedPositionDateColor + 1;
// set color here
dateAdapter.setSelecteditem(setColorDate);
}
public class DateAdapter extends RecyclerView.Adapter<DateAdapter.DateViewHolder> {
private ArrayList<LabelerDate> dateDataList;
private static final int VIEW_TYPE_PADDING = 1;
private static final int VIEW_TYPE_ITEM = 2;
private int paddingWidthDate = 0;
private int selectedItem = -1;
public DateAdapter(ArrayList<LabelerDate> dateData, int paddingWidthDate) {
this.dateDataList = dateData;
this.paddingWidthDate = paddingWidthDate;
}
#Override
public DateAdapter.DateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
if (viewType == VIEW_TYPE_PADDING) {
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
layoutParams.width = paddingWidthDate;
view.setLayoutParams(layoutParams);
}
return new DateViewHolder(view);
}
#Override
public void onBindViewHolder(DateAdapter.DateViewHolder holder, int position) {
LabelerDate labelerDate = dateDataList.get(position);
if (getItemViewType(position) == VIEW_TYPE_ITEM) {
holder.tvDate.setText(labelerDate.getNumber());
holder.tvDate.setVisibility(View.VISIBLE);
Log.d(TAG, "default " + position + ", selected " + selectedItem);
if (position == selectedItem) {
Log.d(TAG, "center" + position);
holder.tvDate.setTextColor(Color.parseColor("#76FF03"));
holder.tvDate.setTextSize(35);
} else {
holder.tvDate.setTextColor(Color.WHITE);
holder.tvDate.setTextSize(18);
}
} else {
holder.tvDate.setVisibility(View.INVISIBLE);
}
}
public void setSelecteditem(int selecteditem) {
this.selectedItem = selecteditem;
notifyDataSetChanged();
}
#Override
public int getItemCount() {
return dateDataList.size();
}
#Override
public int getItemViewType(int position) {
LabelerDate labelerDate = dateDataList.get(position);
if (labelerDate.getType() == VIEW_TYPE_PADDING) {
return VIEW_TYPE_PADDING;
} else {
return VIEW_TYPE_ITEM;
}
}
public class DateViewHolder extends RecyclerView.ViewHolder {
public TextView tvDate;
public DateViewHolder(View itemView) {
super(itemView);
tvDate = (TextView) itemView.findViewById(R.id.txt_date);
}
}
}
private class LabelerDate {
private int type;
private String number;
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
}
}
You can use a LinearSnapHelper
Attach to your recyclerView like
val snapHelper = LinearSnapHelper()
snapHelper.attachToRecyclerView(this)
Then, to get the center view, use
snapHelper.findSnapView(horizontalScrollView.layoutManager)?
As mentioned in the other answer, there is no direct way to do this.
This is probably how you can achieve what you described in the question.
Know the number of items visible on the screen.
Select the middle item programmatically every time the view is scrolled.
Keep a partially transparent image as an overlay on the middle item on the recyclerview. (You'll need to compute the coordinates based on the width of the recycler view or width of the screen and the width of the overlay image you choose to put.
Refresh the selected value in a text view below the recycler view every time there is a scroll.
The image overlays have to be placed in a way they appear connected and as one single control.
For this feature use EcoGallery library:
https://github.com/falnatsheh/EcoGallery
At first, I needed something similar, not this. But I was able to adapt #TranHieu solution to my needs, so I voted up his solution.
I wanted to create full-screen horizontal recyclerview that after user sroll updates scrollPosition to mostVisibleItem.
setup:
private void setUpScrolling() {
mRecyclerVIew.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
mRecyclerVIew.getViewTreeObserver().removeOnPreDrawListener(this);
CustomScrollListener listener = (CustomScrollListener) mScrollListener;
listener.width = mRecyclerVIew.getMeasuredWidth();
listener.dx = 0;
return true;
}
});
}
listener:
private class CustomScrollListener extends OnScrollListener {
private int mLastDx = 0;
int width = 0;
int dx = 0;
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
if (mLastDx != dx) {
scrollToMostVisibleItem();
} else {
dx = 0;
mLastDx = 0;
}
}
super.onScrollStateChanged(recyclerView, newState);
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
this.dx += dx;
}
private void scrollToMostVisibleItem() {
int direction = (dx > 0) ? 1 : -1;
dx = Math.abs(dx);
int shiftCount = Math.round(dx / width);
int pixelShift = dx % width;
if (pixelShift > width / 2) {
shiftCount++;
}
float targetScrollPixels = shiftCount * width;
float finalScrollPixels = (targetScrollPixels - dx) * direction;
if (finalScrollPixels != 0) {
mRecyclerVIew.smoothScrollBy((int) finalScrollPixels, 0);
mLastDx = (int) finalScrollPixels;
dx = 0;
}
}
}
I used another approach in my case.
you can find the deatils here: RecyclerView - How highlight central visible item during scroll1
In my opinion, my solution is more easy than the others.
If someone is looking for a more generic implementation, here is my code based on the answers of this thread:
Add the CenterLinearSnapHelper
public class CenterLinearSnapHelper extends LinearSnapHelper {
//Constants
public static final String TAG = CenterLinearSnapHelper.class.getSimpleName();
//Attributes
private Context context;
private float itemWidth;
private OnPaddingComputationListener listener;
//Constructors
/**
* A linear snap helper which helps centering the items in a recyclerview.
*
* #param itemWidth The (fixed) width of a child view in pixels.
*/
public CenterLinearSnapHelper(float itemWidth) {
this.itemWidth = itemWidth;
}
public void attachToRecyclerView(#Nullable RecyclerView recyclerView,
#NonNull OnPaddingComputationListener listener) throws IllegalStateException {
this.listener = listener;
//Calculates the padding for the first and end item
calculatePadding(recyclerView);
//Create a LinearSnapHelper and attach the recyclerView to it.
attachToRecyclerView(recyclerView);
}
public float getItemWidth() {
return itemWidth;
}
private void calculatePadding(RecyclerView recyclerView) {
if (recyclerView == null)
return;
ViewTreeObserver observer = recyclerView.getViewTreeObserver();
observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
recyclerView.getViewTreeObserver().removeOnPreDrawListener(this);
int finalWidth = recyclerView.getMeasuredWidth();
float padding = (finalWidth - itemWidth) / 2;
listener.onPadding(padding, finalWidth);
return true;
}
});
}
public interface OnPaddingComputationListener {
void onPadding(float padding, int finalWidth);
}
}
In your Activity/Fragment where you create your RecyclerView:
float itemWidth = getResources().getDimension(R.dimen.favorite_room_width);
CenterLinearSnapHelper snapHelper = new CenterLinearSnapHelper(itemWidth);
snapHelper.attachToRecyclerView(binding.listFavorites, (padding, finalWidth) -> {
//Set the adapter
roomAdapter = new RoomAdapter(requireContext(), rooms);
roomAdapter.addPaddingItems((int) padding);
roomAdapter.setOnToggleClickListener(FavoritesFragment.this);
binding.listFavorites.setAdapter(roomAdapter);
});
In your adapter:
public void addPaddingItems(int padding) {
if (padding < 0)
throw new IllegalStateException("Padding cannot be smaller than 0");
this.padding = padding;
//Add 2 new items as the first and last
//NOTE: If you update your existing dataset (e.g add new items), you should redo the calculation!
rooms.add(0, new Room("First"));
rooms.add(rooms.size(), new Room("Last"));
}
#Override
public int getItemViewType(int position) {
if (padding >= 0 && (position == 0 || position == rooms.size() - 1)) {
return VIEW_TYPE_PADDING;
}
return VIEW_TYPE_ITEM;
}
#NonNull
#Override
public RoomViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
ViewFavoriteRoomBinding binding = DataBindingUtil.inflate(inflater, R.layout.view_favorite_room, parent, false);
if (viewType == VIEW_TYPE_PADDING) {
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) binding.getRoot().getLayoutParams();
layoutParams.width = padding;
binding.getRoot().setLayoutParams(layoutParams);
}
RoomViewHolder viewHolder = new RoomViewHolder(context, binding, onToggleClickListener);
viewHolder.getRecyclerView().setRecycledViewPool(viewPool);
return viewHolder;
}
I have to build animation like This.
Sorry I don't have too much reputation to upload image. you can find gif file from the above link.
I have done all this and it works fine on KitKat and LollyPop only but not on the other API version. I am using this library. Can any one please figure out the actual problem
Here is my code. Thanks in advance
public abstract class AbstractSlideExpandableListAdapter extends WrapperListAdapterImpl
{
private View lastOpnUpperView = null;
ArrayList<View> upperViewsList = new ArrayList<View>();
ArrayList<View> lowerViewsList = new ArrayList<View>();
ArrayList<View> circleViewsList = new ArrayList<View>();
private View lastOpen = null;
private int lastOpenPosition = -1;
private int lastOpenItemIndex = 0;
private int animationDuration = 800;
private BitSet openItems = new BitSet();
private final SparseIntArray viewHeights = new SparseIntArray(10);
private ViewGroup parent;
public AbstractSlideExpandableListAdapter(ListAdapter wrapped)
{
super(wrapped);
lastOpenPosition = 0;
openItems.set(lastOpenPosition, true);
}
private OnItemExpandCollapseListener expandCollapseListener;
public void setItemExpandCollapseListener(OnItemExpandCollapseListener listener)
{
expandCollapseListener = listener;
}
public void removeItemExpandCollapseListener()
{
expandCollapseListener = null;
}
public interface OnItemExpandCollapseListener
{
public void onExpand(View itemView, int position);
public void onCollapse(View itemView, int position);
}
private void notifiyExpandCollapseListener(int type, View view, int position)
{
if (expandCollapseListener != null)
{
if (type == ExpandCollapseAnimation.EXPAND)
{
expandCollapseListener.onExpand(view, position);
}
else if (type == ExpandCollapseAnimation.COLLAPSE)
{
expandCollapseListener.onCollapse(view, position);
}
}
}
#Override
public View getView(int position, View view, ViewGroup viewGroup)
{
this.parent = viewGroup;
view = wrapped.getView(position, view, viewGroup);
enableFor(view, position);
return view;
}
public abstract View getExpandToggleButton(View parent);
public abstract View getExpandableView(View parent);
// upperView to expand animation for sequeeze
public abstract View getUpperView(View upperView);
// Lower view to expand and collapse
public abstract View getLowerView(View upperView);
// Get the circle view to hide and show
public abstract View getCircleView(View circleView);
/**
* Gets the duration of the collapse animation in ms. Default is 330ms. Override this method to change the default.
*
* #return the duration of the anim in ms
*/
public int getAnimationDuration()
{
return animationDuration;
}
/**
* Set's the Animation duration for the Expandable animation
*
* #param duration
* The duration as an integer in MS (duration > 0)
* #exception IllegalArgumentException
* if parameter is less than zero
*/
public void setAnimationDuration(int duration)
{
if (duration < 0)
{
throw new IllegalArgumentException("Duration is less than zero");
}
animationDuration = duration;
}
/**
* Check's if any position is currently Expanded To collapse the open item #see collapseLastOpen
*
* #return boolean True if there is currently an item expanded, otherwise false
*/
public boolean isAnyItemExpanded()
{
return (lastOpenPosition != -1) ? true : false;
}
public void enableFor(View parent, int position)
{
View more = getExpandToggleButton(parent);
View itemToolbar = getExpandableView(parent);
View upperView = getUpperView(parent);
View circleView = getCircleView(parent);
View lowerView = getLowerView(parent);
itemToolbar.measure(parent.getWidth(), parent.getHeight());
upperViewsList.add(upperView);
circleViewsList.add(circleView);
lowerViewsList.add(lowerView);
if (position == 0)
{
// lastopenUpperViewTemporary = upperView;
lastOpnUpperView = upperView;
upperView.setVisibility(View.GONE);
lowerView.setVisibility(View.GONE);
}
enableFor(more, upperView, itemToolbar, position);
itemToolbar.requestLayout();
}
private void animateListExpand(final View button, final View target, final int position)
{
target.setAnimation(null);
int type;
if (target.getVisibility() == View.VISIBLE)
{
type = ExpandCollapseAnimation.COLLAPSE;
}
else
{
type = ExpandCollapseAnimation.EXPAND;
}
// remember the state
if (type == ExpandCollapseAnimation.EXPAND)
{
openItems.set(position, true);
}
else
{
openItems.set(position, false);
}
// check if we need to collapse a different view
if (type == ExpandCollapseAnimation.EXPAND)
{
if (lastOpenPosition != -1 && lastOpenPosition != position)
{
if (lastOpen != null)
{
animateWithUpperView(lastOpen, ExpandCollapseAnimation.COLLAPSE, position);
// animateView(lastOpen, ExpandCollapseAnimation.COLLAPSE);
notifiyExpandCollapseListener(ExpandCollapseAnimation.COLLAPSE, lastOpen, lastOpenPosition);
}
openItems.set(lastOpenPosition, false);
}
lastOpen = target;
lastOpenPosition = position;
}
else if (lastOpenPosition == position)
{
lastOpenPosition = -1;
}
// animateView(target, type);
// Expand the view which was collapse
Animation anim = new ExpandCollapseAnimation(target, type);
anim.setDuration(getAnimationDuration());
target.startAnimation(anim);
this.notifiyExpandCollapseListener(type, target, position);
// }
}
private void enableFor(final View button, final View upperView, final View target, final int position)
{
// lastopenUpperViewTemporary = upperView;
if (target == lastOpen && position != lastOpenPosition)
{
// lastOpen is recycled, so its reference is false
lastOpen = null;
}
if (position == lastOpenPosition)
{
// re reference to the last view
// so when can animate it when collapsed
// lastOpen = target;
lastOpen = target;
lastOpnUpperView = upperView;
}
int height = viewHeights.get(position, -1);
if (height == -1)
{
viewHeights.put(position, target.getMeasuredHeight());
updateExpandable(target, position);
}
else
{
updateExpandable(target, position);
}
button.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(final View view)
{
System.out.println("Position: " + position);
if (lastOpenPosition == position)
{
return;
}
System.out.println("Upper View: " + upperView);
Animation anim = new ExpandCollapseUpperViewAnimation(upperViewsList.get(position), ExpandCollapseUpperViewAnimation.COLLAPSE);
anim.setDuration(800);
anim.setAnimationListener(new AnimationListener()
{
#Override
public void onAnimationStart(Animation animation)
{
circleViewsList.get(position).setVisibility(View.GONE);
}
#Override
public void onAnimationRepeat(Animation animation)
{
// TODO Auto-generated method stub
}
#Override
public void onAnimationEnd(Animation animation)
{
// TODO Auto-generated method stub
// upperViewsList.get(position).setVisibility(View.VISIBLE);
animateListExpand(button, target, position);
}
});
upperViewsList.get(position).startAnimation(anim);
// Lower animation
Animation lowerAnim = new ExpandCollapseUpperViewAnimation(lowerViewsList.get(position), ExpandCollapseUpperViewAnimation.COLLAPSE);
lowerAnim.setDuration(800);
lowerViewsList.get(position).startAnimation(lowerAnim);
}
});
}
private void updateExpandable(View target, int position)
{
final LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) target.getLayoutParams();
if (openItems.get(position))
{
target.setVisibility(View.VISIBLE);
params.bottomMargin = 0;
}
else
{
target.setVisibility(View.GONE);
params.bottomMargin = 0 - viewHeights.get(position);
}
}
/**
* Performs either COLLAPSE or EXPAND animation on the target view
*
* #param target
* the view to animate
* #param type
* the animation type, either ExpandCollapseAnimation.COLLAPSE or ExpandCollapseAnimation.EXPAND
*/
private void animateView(final View target, final int type)
{
Animation anim = new ExpandCollapseAnimation(target, type);
anim.setDuration(getAnimationDuration());
anim.setAnimationListener(new AnimationListener()
{
#Override
public void onAnimationStart(Animation animation)
{
}
#Override
public void onAnimationRepeat(Animation animation)
{
}
#Override
public void onAnimationEnd(Animation animation)
{
System.out.println("Animation End");
if (type == ExpandCollapseAnimation.EXPAND)
{
if (parent instanceof ListView)
{
ListView listView = (ListView) parent;
int movement = target.getBottom();
Rect r = new Rect();
boolean visible = target.getGlobalVisibleRect(r);
Rect r2 = new Rect();
listView.getGlobalVisibleRect(r2);
if (!visible)
{
listView.smoothScrollBy(movement, getAnimationDuration());
}
else
{
if (r2.bottom == r.bottom)
{
listView.smoothScrollBy(movement, getAnimationDuration());
}
}
}
}
}
});
target.startAnimation(anim);
}
private void animateWithUpperView(final View target, final int type, final int position)
{
Animation anim = new ExpandCollapseAnimation(target, type);
anim.setDuration(getAnimationDuration());
anim.setAnimationListener(new AnimationListener()
{
#Override
public void onAnimationStart(Animation animation)
{
}
#Override
public void onAnimationRepeat(Animation animation)
{
}
#Override
public void onAnimationEnd(Animation animation)
{
// upperViewsList.get(lastOpenItemForUpperView).setVisibility(View.VISIBLE);
// lastOpenItemForUpperView = position;
Animation expandItemAniamtion = new ExpandCollapseLowerViewAnimation(upperViewsList.get(lastOpenItemIndex), ExpandCollapseUpperViewAnimation.EXPAND);
expandItemAniamtion.setDuration(800);
expandItemAniamtion.setAnimationListener(new AnimationListener()
{
#Override
public void onAnimationStart(Animation animation)
{
}
#Override
public void onAnimationRepeat(Animation animation)
{
// TODO Auto-generated method stub
}
#Override
public void onAnimationEnd(Animation animation)
{
circleViewsList.get(lastOpenItemIndex).setVisibility(View.VISIBLE);
lastOpenItemIndex = position;
}
});
// Lower view animation
Animation lowerAnim = new ExpandCollapseLowerViewAnimation(lowerViewsList.get(lastOpenItemIndex), ExpandCollapseUpperViewAnimation.EXPAND);
lowerAnim.setDuration(800);
upperViewsList.get(lastOpenItemIndex).startAnimation(expandItemAniamtion);
lowerViewsList.get(lastOpenItemIndex).startAnimation(lowerAnim);
}
});
target.startAnimation(anim);
}
/**
* Closes the current open item. If it is current visible it will be closed with an animation.
*
* #return true if an item was closed, false otherwise
*/
public boolean collapseLastOpen()
{
if (isAnyItemExpanded())
{
// if visible animate it out
if (lastOpen != null)
{
animateView(lastOpen, ExpandCollapseAnimation.COLLAPSE);
}
openItems.set(lastOpenPosition, false);
lastOpenPosition = -1;
return true;
}
return false;
}
public Parcelable onSaveInstanceState(Parcelable parcelable)
{
SavedState ss = new SavedState(parcelable);
ss.lastOpenPosition = this.lastOpenPosition;
ss.openItems = this.openItems;
return ss;
}
public void onRestoreInstanceState(SavedState state)
{
if (state != null)
{
this.lastOpenPosition = state.lastOpenPosition;
this.openItems = state.openItems;
}
}
/**
* Utility methods to read and write a bitset from and to a Parcel
*/
private static BitSet readBitSet(Parcel src)
{
BitSet set = new BitSet();
if (src == null)
{
return set;
}
int cardinality = src.readInt();
for (int i = 0; i < cardinality; i++)
{
set.set(src.readInt());
}
return set;
}
private static void writeBitSet(Parcel dest, BitSet set)
{
int nextSetBit = -1;
if (dest == null || set == null)
{
return; // at least dont crash
}
dest.writeInt(set.cardinality());
while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1)
{
dest.writeInt(nextSetBit);
}
}
/**
* The actual state class
*/
static class SavedState extends View.BaseSavedState
{
public BitSet openItems = null;
public int lastOpenPosition = -1;
SavedState(Parcelable superState)
{
super(superState);
}
private SavedState(Parcel in)
{
super(in);
lastOpenPosition = in.readInt();
openItems = readBitSet(in);
}
#Override
public void writeToParcel(Parcel out, int flags)
{
super.writeToParcel(out, flags);
out.writeInt(lastOpenPosition);
writeBitSet(out, openItems);
}
// required field that makes Parcelables from a Parcel
public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>()
{
public SavedState createFromParcel(Parcel in)
{
return new SavedState(in);
}
public SavedState[] newArray(int size)
{
return new SavedState[size];
}
};
}
public static Animation ExpandOrCollapseView(final View v, final boolean expand)
{
try
{
Method m = v.getClass().getDeclaredMethod("onMeasure", int.class, int.class);
m.setAccessible(true);
m.invoke(v, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(((View) v.getParent()).getMeasuredWidth(), MeasureSpec.AT_MOST));
}
catch (Exception e)
{
e.printStackTrace();
}
final int initialHeight = v.getMeasuredHeight();
if (expand)
{
v.getLayoutParams().height = 0;
}
else
{
v.getLayoutParams().height = initialHeight;
}
v.setVisibility(View.VISIBLE);
Animation a = new Animation()
{
#Override
protected void applyTransformation(float interpolatedTime, Transformation t)
{
int newHeight = 0;
if (expand)
{
newHeight = (int) (initialHeight * interpolatedTime);
}
else
{
newHeight = (int) (initialHeight * (1 - interpolatedTime));
}
v.getLayoutParams().height = newHeight;
v.requestLayout();
if (interpolatedTime == 1 && !expand)
v.setVisibility(View.GONE);
}
#Override
public boolean willChangeBounds()
{
return true;
}
};
a.setDuration(1000);
return a;
}}
Luckily I have found the solution. On pre-kitkat the upperView on list item which was gone does not forcelly animate but on kitkat and so on it forcelly animate from gone to visible.
So as the solution we must give a layer of view between of list item and upper view (Which we have to animate).
In this way it will work like charm.
i want to create one screen for my android application in this application i want to create sliding navigation feature which is full visible on swipe (Left to Right ) and when user again swipe (RIght to Left ) it again swipe and close navigation window half and display half screen , i have used navigation drawer but i am not aware with this so please some help me if you have any code so please send me its will be my pleasure .
i am sending one image for reference where .
Find below code create a class AnimationLayout
import usb.terminal.R;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
public class AnimationLayout extends ViewGroup {
public final static int DURATION = 1000;
protected boolean mPlaceLeft = true;
public boolean mOpened;
protected View mSidebar;
protected View mContent;
protected int mSidebarWidth = 100; /*
* assign default value. It will be
* overwrite in onMeasure by Layout xml
* resource.
*/
protected Animation mAnimation;
protected OpenListener mOpenListener;
protected CloseListener mCloseListener;
protected Listener mListener;
protected boolean mPressed = false;
public AnimationLayout(Context context) {
this(context, null);
}
public AnimationLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public void onFinishInflate() {
super.onFinishInflate();
mSidebar = findViewById(R.id.animation_layout_sidebar);
mContent = findViewById(R.id.animation_layout_content);
if (mSidebar == null) {
throw new NullPointerException("no view id = animation_sidebar");
}
if (mContent == null) {
throw new NullPointerException("no view id = animation_content");
}
mOpenListener = new OpenListener(mSidebar, mContent);
mCloseListener = new CloseListener(mSidebar, mContent);
}
#Override
public void onLayout(boolean changed, int l, int t, int r, int b) {
/* the title bar assign top padding, drop it */
int sidebarLeft = l;
if (!mPlaceLeft) {
sidebarLeft = r - mSidebarWidth;
}
mSidebar.layout(sidebarLeft, 0, sidebarLeft + mSidebarWidth,
0 + mSidebar.getMeasuredHeight());
if (mOpened) {
if (mPlaceLeft) {
mContent.layout(l + mSidebarWidth, 0, r + mSidebarWidth, b);
} else {
mContent.layout(l - mSidebarWidth, 0, r - mSidebarWidth, b);
}
} else {
mContent.layout(l, 0, r, b);
}
}
#Override
public void onMeasure(int w, int h) {
super.onMeasure(w, h);
super.measureChildren(w, h);
mSidebarWidth = mSidebar.getMeasuredWidth();
}
#Override
protected void measureChild(View child, int parentWSpec, int parentHSpec) {
/* the max width of Sidebar is 90% of Parent */
if (child == mSidebar) {
int mode = MeasureSpec.getMode(parentWSpec);
int width = (int) (getMeasuredWidth() * 0.9);
super.measureChild(child, MeasureSpec.makeMeasureSpec(width, mode),
parentHSpec);
} else {
super.measureChild(child, parentWSpec, parentHSpec);
}
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (!isOpening()) {
return false;
}
int action = ev.getAction();
if (action != MotionEvent.ACTION_UP
&& action != MotionEvent.ACTION_DOWN) {
return false;
}
/*
* if user press and release both on Content while sidebar is opening,
* call listener. otherwise, pass the event to child.
*/
int x = (int) ev.getX();
int y = (int) ev.getY();
if (mContent.getLeft() < x && mContent.getRight() > x
&& mContent.getTop() < y && mContent.getBottom() > y) {
if (action == MotionEvent.ACTION_DOWN) {
mPressed = true;
}
if (mPressed && action == MotionEvent.ACTION_UP
&& mListener != null) {
mPressed = false;
return mListener.onContentTouchedWhenOpening();
}
} else {
mPressed = false;
}
return false;
}
public void setListener(Listener l) {
mListener = l;
}
/* to see if the Sidebar is visible */
public boolean isOpening() {
return mOpened;
}
public void toggleSidebar() {
if (mContent.getAnimation() != null) {
return;
}
if (mOpened) {
/* opened, make close animation */
if (mPlaceLeft) {
mAnimation = new TranslateAnimation(0, -mSidebarWidth, 0, 0);
} else {
mAnimation = new TranslateAnimation(0, mSidebarWidth, 0, 0);
}
mAnimation.setAnimationListener(mCloseListener);
} else {
/* not opened, make open animation */
if (mPlaceLeft) {
mAnimation = new TranslateAnimation(0, mSidebarWidth, 0, 0);
} else {
mAnimation = new TranslateAnimation(0, -mSidebarWidth, 0, 0);
}
mAnimation.setAnimationListener(mOpenListener);
}
mAnimation.setDuration(DURATION);
mAnimation.setFillAfter(true);
mAnimation.setFillEnabled(true);
mContent.startAnimation(mAnimation);
}
public void openSidebar() {
if (!mOpened) {
toggleSidebar();
}
}
public void closeSidebar() {
if (mOpened) {
toggleSidebar();
}
}
class OpenListener implements Animation.AnimationListener {
View iSidebar;
View iContent;
OpenListener(View sidebar, View content) {
iSidebar = sidebar;
iContent = content;
}
public void onAnimationRepeat(Animation animation) {
}
public void onAnimationStart(Animation animation) {
iSidebar.setVisibility(View.VISIBLE);
}
public void onAnimationEnd(Animation animation) {
iContent.clearAnimation();
mOpened = !mOpened;
requestLayout();
if (mListener != null) {
mListener.onSidebarOpened();
}
}
}
class CloseListener implements Animation.AnimationListener {
View iSidebar;
View iContent;
CloseListener(View sidebar, View content) {
iSidebar = sidebar;
iContent = content;
}
public void onAnimationRepeat(Animation animation) {
}
public void onAnimationStart(Animation animation) {
}
public void onAnimationEnd(Animation animation) {
iContent.clearAnimation();
iSidebar.setVisibility(View.INVISIBLE);
mOpened = !mOpened;
requestLayout();
if (mListener != null) {
mListener.onSidebarClosed();
}
}
}
public interface Listener {
public void onSidebarOpened();
public void onSidebarClosed();
public boolean onContentTouchedWhenOpening();
}
}
create a mainlayout.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<yourpackage..AnimationLayout
android:id="#+id/animation_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<!--Create you Layout which you want and just call in this include-->
<include
android:id="#id/animation_layout_sidebar"
android:layout_width="470dp"
android:layout_height="match_parent"
layout="#layout/my_cook_menu"
android:orientation="vertical" >
</include>
<!--Create you Layout which you want and just call in this include-->
<include
android:id="#id/animation_layout_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
layout="#layout/sound_my_cook"
android:clickable="true"
android:focusable="true"
android:orientation="vertical" >
</include>
</yourpackage.AnimationLayout>
</RelativeLayout>
</RelativeLayout>
create id.xml in values folder
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item type="id" name="animation_layout_sidebar" />
<item type="id" name="animation_layout_content" />
</resources>
write this code in which activity you are calling
public class MainActivity extends Activity implements
AnimationLayout.Listener, OnClickListener{
AnimationDrawable frameAnimation;
AnimationLayout animationLayout;
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.mainlayout);
}
public void onClick(View v) {
try {
animationLayout = (AnimationLayout) findViewById(R.id.animation_layout);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
animationLayout.toggleSidebar();
}
#Override
public void onSidebarOpened() {
// TODO Auto-generated method stub
}
#Override
public void onSidebarClosed() {
// TODO Auto-generated method stub
}
#Override
public boolean onContentTouchedWhenOpening() {
// TODO Auto-generated method stub
return false;
}
}
I haven't tried it yet but I think DrawerLayout recently added to support library could give you what you need. Here is the link for documentation. This is something what Facebook app is using to slide a pannel from left to right. Here is the tutorial.
I want to implement Swipe in ListView like in Samsung Android Phones calling function
I have a list shown in below image :
Now when I swipe right side at some distance of swipe from left i.e 25% distance it just changed background, like call function in Samsung device or like SwipeListView:
need solution for this.
try this code :
main.xml file
<ListView
android:id="#+id/listView1"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ListView>
raw.xml
<TextView
android:id="#+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:gravity="left"
android:text="ABC"
android:textSize="22sp" />
<LinearLayout
android:id="#+id/delete_lay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#55ff0000"
android:visibility="gone" >
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10dp"
android:src="#drawable/ic_menu_delete"
android:visibility="visible" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10dp"
android:gravity="left"
android:text="Deleting..."
android:textColor="#ff0000"
android:textSize="22sp"
android:textStyle="bold" />
</LinearLayout>
MainActivity.java
import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.LayoutParams;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import de.timroes.swipetodismiss.SwipeDismissList;
import de.timroes.swipetodismiss.SwipeDismissList.SwipeDirection;
import de.timroes.swipetodismiss.SwipeDismissList.UndoMode;
import de.timroes.swipetodismiss.SwipeDismissList.Undoable;
public class MainActivity extends Activity {
ArrayList<String> listData;
ListView listView;
ArrayAdapter<String> adapter;
SwipeListAdapter listAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView) findViewById(R.id.listView1);
listData = new ArrayList<String>();
listData.add("item 1");
listData.add("item 2");
listData.add("item 3");
listData.add("item 4");
listData.add("item 5");
listData.add("item 6");
listData.add("item 7");
listData.add("item 8");
listData.add("item 9");
listData.add("item 10");
listData.add("item 11");
listData.add("item 12");
listData.add("item 13");
listData.add("item 14");
listData.add("item 15");
listData.add("item 16");
listData.add("item 17");
listAdapter = new SwipeListAdapter(MainActivity.this);
listView.setAdapter(listAdapter);
listAdapter.notifyDataSetChanged();
SwipeDismissList.OnDismissCallback callback = new SwipeDismissList.OnDismissCallback() {
#Override
public Undoable onDismiss(AbsListView listView, int position) {
View view = (View) listView.getChildAt(0);
view.animate().alpha(1).setDuration(200).translationX(10);
listAdapter.remove(position);
return null;
}
};
UndoMode mode = SwipeDismissList.UndoMode.SINGLE_UNDO;
SwipeDismissList swipeList = new SwipeDismissList(listView, callback,
mode);
swipeList.setSwipeDirection(SwipeDirection.END);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
class SwipeListAdapter extends BaseAdapter {
Context mContext;
public SwipeListAdapter(Context context) {
this.mContext = context;
}
#Override
public int getCount() {
return listData.size();
}
#Override
public Object getItem(int position) {
return listData.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
private void remove(int pos) {
listData.remove(pos);
notifyDataSetChanged();
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
try {
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(
R.layout.raw, null);
}
TextView textView = (TextView) convertView
.findViewById(R.id.name);
textView.setText(listData.get(position));
RelativeLayout layout = (RelativeLayout) convertView
.findViewById(R.id.parentLayout);
LayoutParams params = new LayoutParams(
LayoutParams.MATCH_PARENT, 50);
layout.setLayoutParams(params);
} catch (Exception e) {
e.printStackTrace();
}
return convertView;
}
}
}
SwipeDismissList.java
import static com.nineoldandroids.view.ViewHelper.setAlpha;
import static com.nineoldandroids.view.ViewHelper.setTranslationX;
import static com.nineoldandroids.view.ViewPropertyAnimator.animate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import android.content.Context;
import android.graphics.Rect;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.Button;
import android.widget.ListView;
import android.widget.PopupWindow;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.AnimatorListenerAdapter;
import com.nineoldandroids.animation.ValueAnimator;
public final class SwipeDismissList implements View.OnTouchListener {
// Cached ViewConfiguration and system-wide constant values
private int mSlop;
private int mMinFlingVelocity;
private int mMaxFlingVelocity;
private long mAnimationTime;
// Fixed properties
private AbsListView mListView;
private OnDismissCallback mCallback;
private int mViewWidth = 1; // 1 and not 0 to prevent dividing by zero
// Transient properties
private SortedSet<PendingDismissData> mPendingDismisses = new TreeSet<PendingDismissData>();
private int mDismissAnimationRefCount = 0;
private float mDownX;
private boolean mSwiping;
private VelocityTracker mVelocityTracker;
private int mDownPosition;
private View mDownView;
private boolean mPaused;
private float mDensity;
private boolean mSwipeDisabled;
private UndoMode mMode;
private List<Undoable> mUndoActions;
private Handler mHandler;
private PopupWindow mUndoPopup;
private TextView mUndoText;
private Button mUndoButton;
private SwipeDirection mSwipeDirection = SwipeDirection.BOTH;
private int mAutoHideDelay = 5000;
private String mDeleteString = "Item deleted";
private String mDeleteMultipleString = "%d items deleted";
private boolean mTouchBeforeAutoHide = true;
private int mDelayedMsgId;
View frontView;
View backView;
RelativeLayout layout ;
public enum UndoMode {
SINGLE_UNDO,
MULTI_UNDO,
COLLAPSED_UNDO
};
public enum SwipeDirection {
BOTH,
START,
END
}
public interface OnDismissCallback {
Undoable onDismiss(AbsListView listView, int position);
}
public abstract static class Undoable {
public String getTitle() {
return null;
}
public abstract void undo();
public void discard() { }
}
public SwipeDismissList(AbsListView listView, OnDismissCallback callback) {
this(listView, callback, UndoMode.SINGLE_UNDO);
}
public SwipeDismissList(AbsListView listView, OnDismissCallback callback, UndoMode mode) {
if(listView == null) {
throw new IllegalArgumentException("listview must not be null.");
}
mHandler = new HideUndoPopupHandler();
mListView = listView;
mCallback = callback;
mMode = mode;
ViewConfiguration vc = ViewConfiguration.get(listView.getContext());
mSlop = vc.getScaledTouchSlop();
mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
mAnimationTime = listView.getContext().getResources().getInteger(
android.R.integer.config_shortAnimTime);
mDensity = mListView.getResources().getDisplayMetrics().density;
// -- Load undo popup --
LayoutInflater inflater = (LayoutInflater) mListView.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflater.inflate(R.layout.undo_popup, null);
mUndoButton = (Button)v.findViewById(R.id.undo);
mUndoButton.setOnClickListener(new UndoHandler());
mUndoButton.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
// If user tabs "undo" button, reset delay time to remove popup
mDelayedMsgId++;
return false;
}
});
mUndoText = (TextView)v.findViewById(R.id.text);
mUndoPopup = new PopupWindow(v);
mUndoPopup.setAnimationStyle(R.style.fade_animation);
// Get scren width in dp and set width respectively
int xdensity = (int)(mListView.getContext().getResources().getDisplayMetrics().widthPixels / mDensity);
if(xdensity < 300) {
mUndoPopup.setWidth((int)(mDensity * 280));
} else if(xdensity < 350) {
mUndoPopup.setWidth((int)(mDensity * 300));
} else if(xdensity < 500) {
mUndoPopup.setWidth((int)(mDensity * 330));
} else {
mUndoPopup.setWidth((int)(mDensity * 450));
}
mUndoPopup.setHeight((int)(mDensity * 56));
// -- END Load undo popu --
listView.setOnTouchListener(this);
listView.setOnScrollListener(this.makeScrollListener());
switch(mode) {
case SINGLE_UNDO:
mUndoActions = new ArrayList<Undoable>(1);
break;
default:
mUndoActions = new ArrayList<Undoable>(10);
break;
}
}
private void setEnabled(boolean enabled) {
mPaused = !enabled;
}
public void setAutoHideDelay(int delay) {
mAutoHideDelay = delay;
}
public void setSwipeDirection(SwipeDirection direction) {
mSwipeDirection = direction;
}
public void setUndoString(String msg) {
mDeleteString = msg;
}
public void setUndoMultipleString(String msg) {
mDeleteMultipleString = msg;
}
public void setRequireTouchBeforeDismiss(boolean require) {
mTouchBeforeAutoHide = require;
}
public void discardUndo() {
for(Undoable undoable : mUndoActions) {
undoable.discard();
}
mUndoActions.clear();
mUndoPopup.dismiss();
}
private AbsListView.OnScrollListener makeScrollListener() {
return new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView absListView, int scrollState) {
setEnabled(scrollState != AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
}
#Override
public void onScroll(AbsListView absListView, int i, int i1, int i2) {
}
};
}
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (this.mSwipeDisabled) {
return false;
}
if (mViewWidth < 2) {
mViewWidth = mListView.getWidth();
}
switch (motionEvent.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
if (mPaused) {
return false;
}
// TODO: ensure this is a finger, and set a flag
// Find the child view that was touched (perform a hit test)
Rect rect = new Rect();
int childCount = mListView.getChildCount();
int[] listViewCoords = new int[2];
mListView.getLocationOnScreen(listViewCoords);
int x = (int) motionEvent.getRawX() - listViewCoords[0];
int y = (int) motionEvent.getRawY() - listViewCoords[1];
View child;
for (int i = 0; i < childCount; i++) {
child = mListView.getChildAt(i);
child.getHitRect(rect);
if (rect.contains(x, y)) {
mDownView = child;
break;
}
}
if (mDownView != null) {
mDownX = motionEvent.getRawX();
mDownPosition = mListView.getPositionForView(mDownView);
mVelocityTracker = VelocityTracker.obtain();
mVelocityTracker.addMovement(motionEvent);
}
view.onTouchEvent(motionEvent);
return true;
}
case MotionEvent.ACTION_UP: {
if (mVelocityTracker == null) {
break;
}
float deltaX = motionEvent.getRawX() - mDownX;
mVelocityTracker.addMovement(motionEvent);
mVelocityTracker.computeCurrentVelocity(1000);
float velocityX = Math.abs(mVelocityTracker.getXVelocity());
float velocityY = Math.abs(mVelocityTracker.getYVelocity());
boolean dismiss = false;
boolean dismissRight = false;
if (Math.abs(deltaX) > mViewWidth / 2 && mSwiping) {
dismiss = true;
dismissRight = deltaX > 0;
} else if (mMinFlingVelocity <= velocityX && velocityX <= mMaxFlingVelocity
&& velocityY < velocityX && mSwiping && isDirectionValid(mVelocityTracker.getXVelocity())
&& deltaX >= mViewWidth * 0.2f) {
dismiss = true;
dismissRight = mVelocityTracker.getXVelocity() > 0;
}
if (dismiss) {
// dismiss
final View downView = frontView; // mDownView gets null'd before animation ends
final int downPosition = mDownPosition;
++mDismissAnimationRefCount;
animate(frontView)
.translationX(dismissRight ? mViewWidth : -mViewWidth)
.alpha(0)
.setDuration(mAnimationTime)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
performDismiss(downView, downPosition);
}
});
} else {
// cancel
animate(frontView)
.translationX(0)
.alpha(1)
.setDuration(mAnimationTime)
.setListener(null);
}
mVelocityTracker = null;
mDownX = 0;
mDownView = null;
mDownPosition = ListView.INVALID_POSITION;
mSwiping = false;
if(backView != null) {
backView.setVisibility(View.GONE);
frontView.setVisibility(View.VISIBLE);
setTranslationX(frontView, 0);
layout = null;
backView = null;
frontView = null;
}
break;
}
case MotionEvent.ACTION_MOVE: {
if(mTouchBeforeAutoHide && mUndoPopup.isShowing()) {
// Send a delayed message to hide popup
mHandler.sendMessageDelayed(mHandler.obtainMessage(mDelayedMsgId),
mAutoHideDelay);
}
if (mVelocityTracker == null || mPaused) {
break;
}
mVelocityTracker.addMovement(motionEvent);
float deltaX = motionEvent.getRawX() - mDownX;
// Only start swipe in correct direction
if(isDirectionValid(deltaX)) {
if (Math.abs(deltaX) > mSlop) {
mSwiping = true;
mListView.requestDisallowInterceptTouchEvent(true);
// Cancel ListView's touch (un-highlighting the item)
MotionEvent cancelEvent = MotionEvent.obtain(motionEvent);
cancelEvent.setAction(MotionEvent.ACTION_CANCEL
| (motionEvent.getActionIndex()
<< MotionEvent.ACTION_POINTER_INDEX_SHIFT));
mListView.onTouchEvent(cancelEvent);
}
} else {
mDownX = motionEvent.getRawX();
deltaX = 0;
}
if(layout == null) {
layout = (RelativeLayout) mDownView;
frontView = layout.getChildAt(0);
backView = layout.getChildAt(1);
}
if(deltaX > 50) {
backView.setVisibility(View.VISIBLE);
}
if (mSwiping) {
setTranslationX(frontView, deltaX);
setAlpha(frontView, Math.max(0f, Math.min(1f,
1f - 2f * Math.abs(deltaX) / mViewWidth)));
return true;
}
break;
}
}
return false;
}
private boolean isDirectionValid(float deltaX) {
int rtlSign = 1;
// On API level 17 and above, check if we are in a Right-To-Left layout
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
if(mListView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
rtlSign = -1;
}
}
// Check if swipe has been done in the corret direction
switch(mSwipeDirection) {
default:
case BOTH:
return true;
case START:
return rtlSign * deltaX < 0;
case END:
return rtlSign * deltaX > 0;
}
}
class PendingDismissData implements Comparable<PendingDismissData> {
public int position;
public View view;
public PendingDismissData(int position, View view) {
this.position = position;
this.view = view;
}
#Override
public int compareTo(PendingDismissData other) {
// Sort by descending position
return other.position - position;
}
}
private void performDismiss(final View dismissView, final int dismissPosition) {
// Animate the dismissed list item to zero-height and fire the dismiss callback when
// all dismissed list item animations have completed. This triggers layout on each animation
// frame; in the future we may want to do something smarter and more performant.
final ViewGroup.LayoutParams lp = dismissView.getLayoutParams();
final int originalHeight = dismissView.getHeight();
ValueAnimator animator = ValueAnimator.ofInt(originalHeight, 1).setDuration(mAnimationTime);
animator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
--mDismissAnimationRefCount;
if (mDismissAnimationRefCount == 0) {
// No active animations, process all pending dismisses.
for(PendingDismissData dismiss : mPendingDismisses) {
if(mMode == UndoMode.SINGLE_UNDO) {
for(Undoable undoable : mUndoActions) {
undoable.discard();
}
mUndoActions.clear();
}
Undoable undoable = mCallback.onDismiss(mListView, dismiss.position);
if(undoable != null) {
mUndoActions.add(undoable);
}
mDelayedMsgId++;
}
if(!mUndoActions.isEmpty()) {
changePopupText();
changeButtonLabel();
// Show undo popup
mUndoPopup.showAtLocation(mListView,
Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM,
0, (int)(mDensity * 15));
// Queue the dismiss only if required
if(!mTouchBeforeAutoHide) {
// Send a delayed message to hide popup
mHandler.sendMessageDelayed(mHandler.obtainMessage(mDelayedMsgId),
mAutoHideDelay);
}
}
ViewGroup.LayoutParams lp;
for (PendingDismissData pendingDismiss : mPendingDismisses) {
// Reset view presentation
setAlpha(pendingDismiss.view, 1f);
setTranslationX(pendingDismiss.view, 0);
lp = pendingDismiss.view.getLayoutParams();
lp.height = originalHeight;
pendingDismiss.view.setLayoutParams(lp);
}
mPendingDismisses.clear();
}
}
});
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
lp.height = (Integer) valueAnimator.getAnimatedValue();
dismissView.setLayoutParams(lp);
}
});
mPendingDismisses.add(new PendingDismissData(dismissPosition, dismissView));
animator.start();
}
private void changePopupText() {
String msg = "";
if(mUndoActions.size() > 1 && mDeleteMultipleString != null) {
msg = String.format(mDeleteMultipleString, mUndoActions.size());
} else if(mUndoActions.size() >= 1) {
// Set title from single undoable or when no multiple deletion string
// is given
if(mUndoActions.get(mUndoActions.size() - 1).getTitle() != null) {
msg = mUndoActions.get(mUndoActions.size() - 1).getTitle();
} else {
msg = mDeleteString;
}
}
mUndoText.setText(msg);
}
private void changeButtonLabel() {
String msg;
if(mUndoActions.size() > 1 && mMode == UndoMode.COLLAPSED_UNDO) {
msg = mListView.getResources().getString(R.string.undoall);
} else {
msg = mListView.getResources().getString(R.string.undo);
}
mUndoButton.setText(msg);
}
/**
* Takes care of undoing a dismiss. This will be added as a
* {#link View.OnClickListener} to the undo button in the undo popup.
*/
private class UndoHandler implements View.OnClickListener {
public void onClick(View v) {
if(!mUndoActions.isEmpty()) {
switch(mMode) {
case SINGLE_UNDO:
mUndoActions.get(0).undo();
mUndoActions.clear();
break;
case COLLAPSED_UNDO:
Collections.reverse(mUndoActions);
for(Undoable undo : mUndoActions) {
undo.undo();
}
mUndoActions.clear();
break;
case MULTI_UNDO:
mUndoActions.get(mUndoActions.size() - 1).undo();
mUndoActions.remove(mUndoActions.size() - 1);
break;
}
}
// Dismiss dialog or change text
if(mUndoActions.isEmpty()) {
mUndoPopup.dismiss();
} else {
changePopupText();
changeButtonLabel();
}
mDelayedMsgId++;
}
}
/**
* Handler used to hide the undo popup after a special delay.
*/
private class HideUndoPopupHandler extends Handler {
#Override
public void handleMessage(Message msg) {
if(msg.what == mDelayedMsgId) {
// Call discard on any element
for(Undoable undo : mUndoActions) {
undo.discard();
}
mUndoActions.clear();
mUndoPopup.dismiss();
}
}
}
/**
* Enable/disable swipe.
*/
public void setSwipeDisabled(boolean disabled) {
this.mSwipeDisabled = disabled;
}
}
enjoy!
Use a ListView extension such as android-swipelistview.
You may also need to refer to the demo source. Instructions for setting up the demo can be found here.
To make a phone call when the user swipes left, add the below code to the instance of SwipeListView:
src > activities > SwipeListViewExampleActivity.java > onCreate()
swipeListView.setSwipeListViewListener(new BaseSwipeListViewListener() {
#Override
public void onOpened(int position, boolean toRight) {
if(!toRight) {
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:0123456789"));
startActivity(intent);
}
}
});
For swipe right you can set it to dismiss which should be sufficient for what you are trying to achieve.
res > layout > swipe-list-view-activity.xml > SwipeListView
swipe:swipeActionRight="dismiss"
you can use this library https://github.com/timroes/EnhancedListView An Android ListView with enhanced functionality (e.g. Swipe To Dismiss or Undo).