I have a RecyclerView that has a grid of items. Upon clicking on an item, it highlights.
I also want that when the user swipes right the a 'next' method is called, and when the user swipes left, a 'previous' method is called.
However, the two don't work out with each other, as each intercepts the other's events.
How do I get them to work together?
This is my code:
RecyclerView Adapter
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
myHolder = holder as MyView;
myHolder.mMainView.SetOnClickListener(this);
if (selected_position == position)
{
holder.ItemView.SetBackgroundColor(Color.LightGray);
}
else
{
holder.ItemView.SetBackgroundColor(Color.Transparent);
}
}
public void OnClick(View v)
{
int position = mRecyclerView.GetChildLayoutPosition((View)sender);
// Updating old as well as new positions
NotifyItemChanged(selected_position);
selected_position = position;
NotifyItemChanged(selected_position);
}
Fragment that contains the RecyclerView
calendarRecyclerView.SetOnTouchListener(this);
public bool OnTouch(View v, MotionEvent e)
{
switch (e.Action)
{
case MotionEventActions.Down:
x1 = e.GetX();
break;
case MotionEventActions.Up:
x2 = e.GetX();
float deltaX = x2 - x1;
if (Math.Abs(deltaX) > MIN_DISTANCE)
{
// Left to Right swipe action
if (x2 > x1)
{
NextMonth();
}
// Right to left swipe action
else
{
PreviousMonth();
}
}
break;
}
return false;
}
Because I put return false in the OnTouch event, the item's click event is fired. However, the MouseDown event doesn't fire in OnTouch, preventing swiping back detection (beacuse x1 is always 0).
OnTouch event gets called on the first click, and the OnClick gets called only on the second click
Because MotionEventActions.Down and OnClickconflict. As a workaround I suggest you to change the background color at the MotionEventActions.Down event.
Create your own click listener
Call the listener when you touch down your items.
The listener will callback to MainActivity to notify the item changed.
At the same time the touch event will called.
I have set the OnTouchListener in the viewholder :
public class MyViewHolder:RecyclerView.ViewHolder,IOnTouchListener
{
private TextView textView;
private MyItemClickListener mListener;
private Context myContext;
float x1 = 0;
float x2 = 0;
public TextView TextView { get { return textView; } }
public MyViewHolder(View v, MyItemClickListener mItemClickListener) : base(v)
{
textView = v.FindViewById<TextView>(Resource.Id.itemText);
mListener = mItemClickListener;
v.SetOnTouchListener(this);
}
public MyViewHolder(View v, MyItemClickListener mItemClickListener, Context myContext) : this(v, mItemClickListener)
{
this.myContext = myContext;
}
public bool OnTouch(View v, MotionEvent e)
{
switch (e.Action)
{
case MotionEventActions.Down:
x1 = e.GetX();
if (mListener != null)
{
mListener.OnItemClick(v, Position);
}
break;
case MotionEventActions.Up:
x2 = e.GetX();
float deltaX = x2 - x1;
if (Math.Abs(deltaX) > 5)
{
// Left to Right swipe action
if (x2 > x1)
{
NextMonth(v);
}
// Right to left swipe action
else
{
PreviousMonth(v);
}
}
break;
}
return true;
}
public Boolean NextMonth(View v)
{
Toast.MakeText(myContext, "NextMonth called", ToastLength.Short).Show();
return true;
}
public Boolean PreviousMonth(View v)
{
Toast.MakeText(myContext, "PreviousMonth called", ToastLength.Short).Show();
return true;
}
}
Defined the click interface :
public interface MyItemClickListener
{
void OnItemClick(View view, int postion);
}
set the click callback in the MainActivity to change the background color:
public class MainActivity : Activity,MyItemClickListener
{
RecyclerView mRecyclerView;
RecyclerView.LayoutManager mLayoutManager;
CustomAdapter mAdapter;
string[] dataSet;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
InitDataSet();
SetContentView(Resource.Layout.Main);
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView = FindViewById<RecyclerView>(Resource.Id.recyclerView);
mRecyclerView.SetLayoutManager(mLayoutManager);
mAdapter = new CustomAdapter(dataSet,this);
mAdapter.setOnItemClickListener(this);
mRecyclerView.SetAdapter(mAdapter);
//mRecyclerView.SetOnTouchListener(this);
}
public void InitDataSet()
{
dataSet = new string[60];
for (int i = 0; i < 60; i++)
{
dataSet[i] = "This is element #" + i;
}
}
public void OnItemClick(View view, int postion)
{
mAdapter.NotifyItemChanged(CustomAdapter.selected_position);
CustomAdapter.selected_position = postion;
mAdapter.NotifyItemChanged(postion);
}
}
}
Note: Keep your finger move fast, if the speed is slow enough the MotionEventActions.Down will not be called.
Github souce code
Screen shot:
Try this in your onClick
public void OnClick(View v)
{
int position = mRecyclerView.GetChildLayoutPosition((View)sender);
int oldPosition = selectedPosition;
selected_position = position;
// Updating old as well as new positions
NotifyItemChanged(oldPosition);
NotifyItemChanged(selected_position);
}
Notice that you have to change the selected position before updating both items
Related
Here I clicked on the item to change item background and color. I've stored the clicked item value in the database and change the layout color and text color and recreating the adapter and showing the list again while refreshing.
But layout colors not changed when I get its position. Please show the right path to handle the set of background item color always.
public class LoadVehicleTypeAdapter extends RecyclerView.Adapter<LoadVehicleTypeAdapter.CarTypesHolder> {
private List<TaxiTypeResponse.Message> CarTypesModelsList;
private Context mContext;
VehicleTypeView vehicleTypeView;
int I = -1;
int idd = 0;
int II = 0;
Activity activity;
GoogleMap map;
List<VehicleClick> list;
private SparseBooleanArray selectedItems;
public class CarTypesHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public CustomTextView mCarType;
public CircleImageView mCarTypeImage;
LinearLayout llRoot;
CardView cardView;
setOnitemclick listener;
public void setOnItemClickListner(setOnitemclick listener) {
this.listener = listener;
}
public CarTypesHolder(View view) {
super(view);
mCarType = (CustomTextView) view.findViewById(R.id.frag_cartypes_inflated_name);
mCarTypeImage = (CircleImageView) view.findViewById(R.id.frag_cartype_inflated_frameImage);
llRoot = (LinearLayout) view.findViewById(R.id.root1);
selectedItems = new SparseBooleanArray();
view.setOnClickListener(this);
}
#Override
public void onClick(View v) {
listener.ImageClick(v, getAdapterPosition());
}
}
public LoadVehicleTypeAdapter(Context context, List<TaxiTypeResponse.Message> CarTypesModelsList, VehicleTypeView vehicleTypeView, Activity activity, GoogleMap map, List<VehicleClick> lists) {
this.CarTypesModelsList = CarTypesModelsList;
mContext = context;
this.vehicleTypeView = vehicleTypeView;
this.activity = activity;
this.map = map;
this.list = lists;
}
#Override
public CarTypesHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.frag_cartype_inflated_view, parent, false);
return new CarTypesHolder(itemView);
}
#SuppressLint("ResourceType")
#Override
public void onBindViewHolder(final CarTypesHolder holder, int position) {
if (list.size() != 0) {
II = Integer.parseInt(list.get(0).RideId);
//setSelection(Integer.parseInt(list.get(0).RideId));
}
if (II == position) {
holder.llRoot.setBackgroundColor(Color.parseColor("#999999"));
holder.mCarType.setTextColor(Color.parseColor("#FFFFFF"));
} else {
holder.llRoot.setBackgroundColor(Color.parseColor("#f3f3f3"));
holder.mCarType.setTextColor(Color.parseColor("#2196F3"));
}
SharedPreferences sharedPreferences = activity.getSharedPreferences("mSelected", Context.MODE_PRIVATE);
TaxiTypeResponse.Message carTypesModel = CarTypesModelsList.get(position);
holder.mCarType.setText(carTypesModel.getName());
holder.mCarTypeImage.setBackgroundResource(R.drawable.wait);
int color = Color.parseColor(PreferenceHandler.readString(mContext, PreferenceHandler.SECONDRY_COLOR, "#006fb6"));
holder.mCarType.setTextColor(color);
holder.setOnItemClickListner(new setOnitemclick() {
#Override
public void ImageClick(View v, int position1) {
I = position1;
notifyDataSetChanged();
try {
if (list.size() != 0) {
VehicleTypeFragment.myAppRoomDataBase.userDao().delete();
list.clear();
}
VehicleClick vehicleClick = new VehicleClick();
vehicleClick.setRideId(String.valueOf(position1));
VehicleTypeFragment.myAppRoomDataBase.userDao().insert(vehicleClick);
list.add(vehicleClick);
} catch (Exception e) {
}
}
});
if (I == position) {
holder.llRoot.setBackgroundColor(Color.parseColor("#999999"));
holder.mCarType.setTextColor(Color.parseColor("#ffffff"));
Animation bounce = AnimationUtils.loadAnimation(mContext, R.anim.bounce);
holder.llRoot.startAnimation(bounce);
} else {
holder.llRoot.setBackgroundColor(Color.parseColor("#f3f3f3"));
holder.mCarType.setTextColor(Color.parseColor("#2196F3"));
}
Picasso.with(mContext).load(carTypesModel.getImagePath()).into(holder.mCarTypeImage);
}
#Override
public long getItemId(int position) {
return CarTypesModelsList.get(position).getID();
}
#Override
public int getItemCount() {
return CarTypesModelsList.size();
}
public void setSelection(int position) {
II = position;
//notifyDataSetChanged();
}
public interface setOnitemclick {
void ImageClick(View view, int position);
}
#Override
public int getItemViewType(int position) {
return position;
}
}
I am not sure what did you mean by refreshing your list. I am guessing that you are recreating the adapter and showing the list again while you are refreshing. Hence the value of I is initialized with -1 each time you are creating the adapter.
You need to do the initialization as follows. Please consider the following is a pseudo code and you need to implement this of your own.
// While declaring your I
// int I = -1;
int I = getTheStoredValueFromDatabase(); // If there is no entry in database, getTheStoredValueFromDatabase function will return -1
I hope you get the idea. You might consider doing the same for other stored values.
for keep track record you need to add Boolean variable in TaxiTypeResponse.Message boolean isClick=false; and toggle this in
holder.setOnItemClickListner(new setOnitemclick() {
#Override
public void ImageClick(View v, int position) {
CarTypesModelsList.get(position).isClick=!CarTypesModelsList.get(position).isClick;
notifyDataSetChanged();
}
}
and modify below code as follow
if (CarTypesModelsList.get(position).isClick) {
holder.llRoot.setBackgroundColor(Color.parseColor("#999999"));
holder.mCarType.setTextColor(Color.parseColor("#ffffff"));
Animation bounce = AnimationUtils.loadAnimation(mContext, R.anim.bounce);
holder.llRoot.startAnimation(bounce);
}
else{
holder.llRoot.setBackgroundColor(Color.parseColor("#f3f3f3"));
holder.mCarType.setTextColor(Color.parseColor("#2196F3"));
}
Note: onBindViewHolder() is not a place to implement the click
listeners, but I am just providing you the logic for how to implement
single selection in recyclerView.
Now lets jump to the solution,
simply follow the below tutorial and change the variable, fields, and background according to your need, you have to implement the below method in onBindViewHolder() method of RecyclerView
First, initialize the lastClickedPosition and isclicked
int lastPositionClicked = -1;
boolean isClicked = false;
#Override
public void onBindViewHolder(#NonNull final MyViewHolder holder, final int position) {
holder.YOUR_VIEW.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// store the position which you have just clicked and you will change the background of this clicked view
lastPositionClicked = position;
isClicked = true;
// need to refresh the adapter
SlabAdapter.this.notifyDataSetChanged();
}
});
// only change the background of last clicked item
if (isClicked && position == lastPositionClicked) {
// change clicked view background color
} else {
// otherwise set the default color for all positions
}
}
let me know if this works.
on BindViewHolder method you'll use this code and set I=0 on globally
#SuppressLint("ResourceType")
#Override
public void onBindViewHolder(final CarTypesHolder holder, int position) {
SharedPreferences sharedPreferences = activity.getSharedPreferences("mSelected", Context.MODE_PRIVATE);
TaxiTypeResponse.Message carTypesModel = CarTypesModelsList.get(position);
holder.mCarType.setText(carTypesModel.getName());
holder.mCarTypeImage.setBackgroundResource(R.drawable.wait);
int color = Color.parseColor(PreferenceHandler.readString(mContext, PreferenceHandler.SECONDRY_COLOR, "#006fb6"));
holder.mCarType.setTextColor(color);
holder.setOnItemClickListner(new setOnitemclick() {
#Override
public void ImageClick(View v, int position1) {
I = position1;
notifyDataSetChanged();
try {
if (list.size() != 0) {
VehicleTypeFragment.myAppRoomDataBase.userDao().delete();
list.clear();
}
VehicleClick vehicleClick = new VehicleClick();
vehicleClick.setRideId(String.valueOf(position1));
VehicleTypeFragment.myAppRoomDataBase.userDao().insert(vehicleClick);
list.add(vehicleClick);
} catch (Exception e) {
}
}
});
if (I == position) {
holder.llRoot.setBackgroundColor(Color.parseColor("#999999"));
holder.mCarType.setTextColor(Color.parseColor("#ffffff"));
Animation bounce = AnimationUtils.loadAnimation(mContext, R.anim.bounce);
holder.llRoot.startAnimation(bounce);
} else {
holder.llRoot.setBackgroundColor(Color.parseColor("#f3f3f3"));
holder.mCarType.setTextColor(Color.parseColor("#2196F3"));
}
Picasso.with(mContext).load(carTypesModel.getImagePath()).into(holder.mCarTypeImage);
}
I am trying to implement a horizontal recycleview with right and left arrow indicators. So what happens is if one clicks the right arrow next item should appear and if one clicks the left arrow the previous item should appear and also at the end of the list the left arrow should disappear. I have no Idea how ti implement this. can someone help me out? Below is my Horizontal Recyclerview adapter.
public class DialogRecyclerViewAdapter extends RecyclerView.Adapter<DialogRecyclerViewAdapter.ViewHolder> {
Context context;
List<UploadImage> dataAdapters;
private SharedPreferences.Editor mSharedPrefEditor;
ImageLoader imageLoader;
public DialogRecyclerViewAdapter(List<UploadImage> getDataAdapter, Context context){
super();
this.dataAdapters = getDataAdapter;
this.context = context;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.cardview, parent, false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
#Override
public void onBindViewHolder(ViewHolder Viewholder, int position) {
final UploadImage dataAdapterOBJ = dataAdapters.get(position);
imageLoader = ImageAdapter.getInstance(context).getImageLoader();
imageLoader.get(dataAdapterOBJ.getImage(),
ImageLoader.getImageListener(
Viewholder.VollyImageView,//Server Image
R.drawable.loading_1,//Before loading server image the default showing image.
android.R.drawable.ic_dialog_alert //Error image if requested image dose not found on server.
)
);
Viewholder.VollyImageView.setImageUrl(dataAdapterOBJ.getImage(), imageLoader);
Viewholder.ImageTitleTextView.setText(dataAdapterOBJ.getBrand_name());
Viewholder.garment_price.setText(dataAdapterOBJ.getGarment_price());
Viewholder.VollyImageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(MihuChatApplication.getInstance().getContext());
mSharedPrefEditor = sharedPref.edit();
mSharedPrefEditor.putString(Constants.KEY_FROM_CHAT, "fromChatWIndow").apply();
Intent i=new Intent(MihuChatApplication.getInstance().getContext(), DetailsNewActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//PACK DATA TO SEND
i.putExtra("image_title",dataAdapterOBJ.getGarment_name());
i.putExtra("image_url",dataAdapterOBJ.getImage_full());
i.putExtra("desc_text", dataAdapterOBJ.getDesc_text());
//i.putExtra("image_url2", imageLarger);
i.putExtra("image_price", dataAdapterOBJ.getGarment_price());
//i.putExtra("disc_price", disc_price);
//open activity
MihuChatApplication.getInstance().getApplicationContext().startActivity(i);
}
});
}
#Override
public int getItemCount() {
return dataAdapters.size();
}
class ViewHolder extends RecyclerView.ViewHolder{
public TextView ImageTitleTextView, garment_price;
public NetworkImageView VollyImageView ;
public ViewHolder(View itemView) {
super(itemView);
garment_price = (TextView) itemView.findViewById(R.id.garment_price);
ImageTitleTextView = (TextView) itemView.findViewById(R.id.ImageNameTextView) ;
VollyImageView = (NetworkImageView) itemView.findViewById(R.id.VolleyImageView) ;
}
}
}
Thanks in advance.
Try the following
here img_LeftScroll is the left imageview and img_right_scroll is the right imageview between the horizontal list, rv_horizontal is the horizontallist view
then onclick of the image view do the below, hope it works
img_LeftScroll.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (horizontalLayoutManagaer.findFirstVisibleItemPosition() > 0) {
rv_horizontal.smoothScrollToPosition(horizontalLayoutManagaer.findFirstVisibleItemPosition() - 1);
} else {
rv_horizontal.smoothScrollToPosition(0);
}
}
});
img_right_scroll.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
rv_horizontal.smoothScrollToPosition(horizontalLayoutManagaer.findLastVisibleItemPosition() + 1);
}
});
Horizontal Recycler view with left and right arrow Indicators --I have Done this By creating a custom Layout with recyclerVIew and two arrow images --
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
switch (newState) {
case SCROLL_STATE_DRAGGING:
//Indicated that user scrolled.
programaticallyScrolled = false;
break;
case SCROLL_STATE_IDLE:
if (!programaticallyScrolled) {
currentVisibleItem = linearLayoutManager.findFirstCompletelyVisibleItemPosition();
handleWritingViewNavigationArrows(false);
}
break;
default:
break;
}
}
});
Hide/Show navigation arrows
/**
* Handles the arrows visibility based on current visible items and scrolls the
* current visibility item based on param scroll.
*
* Scroll - is False if User scrolls the Reycler Manually
* - is True means User used arrows to navigate
*
* #param scroll
*/
private void handleWritingViewNavigationArrows(boolean scroll) {
if (currentVisibleItem == (recyclerView.getAdapter().getItemCount() - 1)) {
rightArrow.setVisibility(View.GONE);
leftArrow.setVisibility(View.VISIBLE);
} else if (currentVisibleItem != 0) {
rightArrow.setVisibility(View.VISIBLE);
leftArrow.setVisibility(View.VISIBLE);
} else if (currentVisibleItem == 0) {
leftArrow.setVisibility(View.GONE);
rightArrow.setVisibility(View.VISIBLE);
}
if (scroll) {
recyclerView.smoothScrollToPosition(currentVisibleItem);
}
}
Check the example for the reference hope it helps u
https://github.com/RameshBhupathi/RecyclerViewWithLeftAndRightArrowsExample
This code works in my case:
btn_to_right.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
p = linearLayoutManager.findFirstVisibleItemPosition() - 1;
recyclerview.smoothScrollToPosition( p);
checkVisibility();
}
});
btn_to_left.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
p = linearLayoutManager.findLastVisibleItemPosition() + 1;
recyclerview.smoothScrollToPosition(p);
checkVisibility();
}
});
public void checkVisibility() {
if (p < 1) {
btn_to_right.setVisibility(View.GONE);
btn_to_left.setVisibility(View.VISIBLE);
} else if (p >= (recyclerview.getAdapter().getItemCount() - 1)) {
btn_to_right.setVisibility(View.VISIBLE);
btn_to_left.setVisibility(View.GONE);
} else {
btn_to_right.setVisibility(View.VISIBLE);
btn_to_left.setVisibility(View.VISIBLE);
}
}
Much simpler approach for hiding/showing arrows:
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(#NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (manager.findFirstCompletelyVisibleItemPosition()==0){
leftArrow.setVisibility(View.INVISIBLE);
}else{
leftArrow.setVisibility(View.VISIBLE);
}
if (manager.findLastCompletelyVisibleItemPosition()==list.size()-1){
rightArrow.setVisibility(View.INVISIBLE);
}else
rightArrow.setVisibility(View.VISIBLE);
}
#Override
public void onScrolled(#NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
}
});
Hi I want to show a layout when I swipe up. I want that would be animated. When I swipe up below this layout appear another layout. I did this but it not works like I want to. A new layout show after all swipe not when I am swiping .
public class RelativeLayoutTouchListener implements View.OnTouchListener {
static final String logTag = "ActivitySwipeDetector";
private Activity activity;
static final int MIN_DISTANCE = 100;// TODO change this runtime based on screen resolution. for 1920x1080 is to small the 100 distance
private float downX, downY, upX, upY;
private View view;
// private MainActivity mMainActivity;
public RelativeLayoutTouchListener(MainActivity mainActivity, View view) {
activity = mainActivity;
this.view = view;
}
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
Log.e("sdasdas", "dsfdsfdsfsdf");
downX = event.getX();
downY = event.getY();
return true;
}
case MotionEvent.ACTION_UP: {
upX = event.getX();
upY = event.getY();
float deltaX = downX - upX;
float deltaY = downY - upY;
final ViewGroup.LayoutParams params = MainActivity.relativeLayout1.getLayoutParams();
if (deltaY < 0) {
params.height = (int) activity.getResources().getDimension(R.dimen.dimen_entry_in_dp);
// relativeLayout1.setLayoutParams(params);
view.setVisibility(View.GONE);
collapse(relativeLayout1, 400, params.height);
MainActivity.listView1.setVisibility(View.GONE);
} else {
// params.height = RelativeLayout.LayoutParams.WRAP_CONTENT;
params.height = (int) activity.getResources().getDimension(R.dimen.dimen_entry_in_dp2);
// MainActivity.relativeLayout1.setLayoutParams(params);
expand(relativeLayout1, 400, params.height);
view.setVisibility(View.VISIBLE);
MainActivity.listView1.setVisibility(View.VISIBLE);
}
return false; // no swipe horizontally and no swipe vertically
}
// case MotionEvent.ACTION_UP:
}
return false;
}
public static void expand(final View v, int duration, int targetHeight) {
int prevHeight = v.getHeight();
v.setVisibility(View.VISIBLE);
ValueAnimator valueAnimator = ValueAnimator.ofInt(prevHeight, targetHeight);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
v.getLayoutParams().height = (int) animation.getAnimatedValue();
v.requestLayout();
}
});
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.setDuration(duration);
valueAnimator.start();
}
public static void collapse(final View v, int duration, int targetHeight) {
int prevHeight = v.getHeight();
ValueAnimator valueAnimator = ValueAnimator.ofInt(prevHeight, targetHeight);
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
v.getLayoutParams().height = (int) animation.getAnimatedValue();
v.requestLayout();
}
});
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.setDuration(duration);
valueAnimator.start();
}
}
Use a Dialog class to achieve the animation with ease:
public class CustomDialog extends Dialog {
private Fragment fragment;
private Activity activity;
public CustomDialog(Fragment fragment) {
super(fragment.getContext(), R.style.DialogTheme);
this.fragment=fragment;
}
public CustomDialog(Activity activity){
super(activity, R.style.DialogTheme);
this.activity = activity;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.dialog_photo_upload);
Window window = getWindow();
WindowManager.LayoutParams wlp = window.getAttributes();
wlp.width = WindowManager.LayoutParams.MATCH_PARENT;
wlp.gravity = Gravity.BOTTOM;
wlp.windowAnimations = R.style.popupWindowDropDownAnimation;
window.setAttributes(wlp);
window.setBackgroundDrawable(new ColorDrawable(android.graphics.Color.TRANSPARENT));
}
Call this from your Fragment or Activity whenever you want to show it:
private CustomDialog mCustomDialog = new CustomDialog(this).show();
Call this to hide it:
mCustomDialog.dismiss();
It'll produce something like
I have a strange problem - I am using an activity with a ViewSwitcher, this ViewSwitcher has a ListView and a GridView which use the same ArrayAdapter{SomeContent}.
I have made the Adapters so that I can pass them the R.layout.value that they use to inflate - this is how I manage consistency across ListView / GridView since I want somewhat different views in these two forms.
And all of this works somewhat perfectly, just not for the first time.
No, for the first time that I run my application in GRID display mode, my gridview tries to use the wrong XML, and ends up trying to use (recycle?) the ListView's XML. I know so because GridView's XML has no reference of checkboxes and I see them constantly.
But when I leave this screen (not even going to say 'activity') and come back to it, everything is inflated perfectly. List always works off the bat, GridViews ~80% don't work only first time.
Any ideas?
Here is some code to get you started.
ADAPTER:
public class AudioAdapter extends ArrayAdapter<MusicContent>
{
private Context context;
private ImageView albumArt;
private TextView songName;
private TextView artistName;
private TextView albumName;
private TextView genre;
private TextView duration;
private int viewToUse;
private CheckBox checkbox;
private OnItemClickListener clickListener;
private OnItemSelectedListener focusListener;
private List<MusicContent> content = new ArrayList<MusicContent>();
public AudioAdapter(Context context, int textViewResourceId, List<MusicContent> objects,
OnItemClickListener clickListener, OnItemSelectedListener focusListener)
{
super(context, textViewResourceId, objects);
this.context = context;
this.content = objects;
this.clickListener = clickListener;
this.focusListener = focusListener;
this.viewToUse = textViewResourceId;
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
View row = convertView;
/**
* Removed following optimization on purpose due to dynamically using
* different layouts which may result in wrong view being recycled for
* use
*/
//removing it fixed nothing really
if (row == null)
{
// ROW INFLATION
LayoutInflater inflater = (LayoutInflater) this.getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
row = inflater.inflate(viewToUse, parent, false);
}
// initiate helpers for onClick hack
final AdapterView fParent = (AdapterView) parent;
final View fView = row;
final int fInt = position;
final long fLong = row.getId();
// Get item
MusicContent item = getItem(position);
if (item == null)
return row;
RelativeLayout root = (RelativeLayout) row.findViewById(R.id.list_cell_layout);
// perform a series of checks to maintain customizability
albumArt = (ImageView) row.findViewById(R.id.list_cell_image);
if (albumArt != null)
{
if (item.hasAlbumArt()) {
albumArt.setImageBitmap(item.getAlbumBitmap(context));
}
else
albumArt.setImageDrawable(context.getResources().getDrawable(
R.drawable.ic_music_album));
}
LinearLayout checkLL = (LinearLayout) row.findViewById(R.id.list_cell_music_info);
if (checkLL != null)
{
// display some song info
songName = (TextView) checkLL.findViewById(R.id.list_cell_title);
if (songName != null)
{
songName.setText(item.getDisplayName());
songName.setVisibility(View.VISIBLE);
}
// attach artificial OnItemClick and OnItemSelected listeners
if (clickListener != null)
{
OnClickListener cross = new OnClickListener() {
#Override
public void onClick(View v) {
Log.d("SHARK", "internal onClick from adapter!!" + fView);
clickListener.onItemClick(fParent, fView, fInt, fLong);
}
};
checkLL.setOnClickListener(cross);
}
if (focusListener != null)
{
OnFocusChangeListener cross = new OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus)
focusListener.onItemSelected(fParent, fView, fInt, fLong);
}
};
checkLL.setOnFocusChangeListener(cross);
}
checkLL = (LinearLayout) row.findViewById(R.id.list_cell_artist_info);
if (checkLL != null)
{
// display artist info too
artistName = (TextView) checkLL.findViewById(R.id.list_cell_artist_name);
if (artistName != null)
artistName.setText(item.getArtist());
albumName = (TextView) checkLL.findViewById(R.id.list_cell_album);
if (albumName != null)
albumName.setText(item.getAlbum());
duration = (TextView) checkLL.findViewById(R.id.list_cell_duration);
if (duration != null)
duration.setText(item.getDurationString());
genre = (TextView) checkLL.findViewById(R.id.list_cell_genre);
if (genre != null)
genre.setText(item.getGenre());
// block focus on descendants
checkLL.setDescendantFocusability(RelativeLayout.FOCUS_BLOCK_DESCENDANTS);
// attach artificial listeners
if (clickListener != null)
{
OnClickListener cross = new OnClickListener() {
#Override
public void onClick(View v) {
Log.d("SHARK", "internal onClick from adapter!!" + fView);
clickListener.onItemClick(fParent, fView, fInt, fLong);
}
};
checkLL.setOnClickListener(cross);
}
if (focusListener != null)
{
OnFocusChangeListener cross = new OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus)
focusListener.onItemSelected(fParent, fView, fInt, fLong);
}
};
checkLL.setOnFocusChangeListener(cross);
}
}
// FrameLayout checkFL = (FrameLayout)
// row.findViewById(R.id.endoflineinfo);
checkLL = (LinearLayout) row.findViewById(R.id.endoflineinfo);
if (checkLL != null)
{
checkbox = (CheckBox) checkLL.findViewById(R.id.in_playlist);
if (checkbox != null)
{
checkbox.setVisibility(View.VISIBLE);
if (item.getPlaylist() != null)
checkbox.setChecked(!item.getPlaylist().isEmpty());
}
checkLL.setDescendantFocusability(RelativeLayout.FOCUS_BLOCK_DESCENDANTS);
if (clickListener != null)
{
OnClickListener cross = new OnClickListener() {
#Override
public void onClick(View v) {
Log.d("SHARK", "internal onClick from adapter!!" + fView);
clickListener.onItemClick(fParent, fView, fInt, fLong);
}
};
checkLL.setOnClickListener(cross);
}
if (focusListener != null)
{
OnFocusChangeListener cross = new OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus)
focusListener.onItemSelected(fParent, fView, fInt, fLong);
}
};
checkLL.setOnFocusChangeListener(cross);
}
}
}
// magic happens where we bind an OnItemClick call to OnClick
root.setDescendantFocusability(RelativeLayout.FOCUS_BLOCK_DESCENDANTS);
if (clickListener != null)
{
OnClickListener cross = new OnClickListener() {
#Override
public void onClick(View v) {
Log.d("SHARK", "internal onClick from adapter!!");
clickListener.onItemClick(fParent, fView, fInt, fLong);
}
};
// assign this listener
root.setOnClickListener(cross);
}
if (focusListener != null)
{
OnFocusChangeListener cross = new OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus)
focusListener.onItemSelected(fParent, fView, fInt, fLong);
}
};
root.setOnFocusChangeListener(cross);
}
return row;
}
As for why so many ifs and checks - it has to be able to survive different underlying XMLs with missing elements (customizability) and don't worry too much about the onClick / onFocus hacks - they're much needed workarounds....
After looking into how my views are recycled, I came to a conclusion that I was running the app in a different viewmode than it was last left in - this caused GRID views to be saved up and used in LIST, or vice-versa.
After revising, I made sure to restart the app in the same mode that it was previously used or force the new views upon restart be recreating the adapter and reassigning it to my view.
I'm developing an application in which I have to use viewpager and after all items in viewpager is finished I have to call an activity. I'm not able to get event listener for this. Here is what I have been refering too:
https://github.com/chiuki/android-swipe-image-viewer/blob/master/src/com/sqisland/android/swipe_image_viewer/MainActivity.java
Here is what I have done so far:
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewPager viewPager = (ViewPager) findViewById(R.id.view_pager);
ImagePagerAdapter adapter = new ImagePagerAdapter();
viewPager.setAdapter(adapter);
OnPageChangeListener mListener = new OnPageChangeListener() {
#Override
public void onPageSelected(int arg0) {
// TODO Auto-generated method stub
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
#Override
public void onPageScrollStateChanged(int arg0) {
Log.i("Doing something here", "On Scroll state changed");
}
};
viewPager.setOnPageChangeListener(mListener);
}
private class ImagePagerAdapter extends PagerAdapter {
private int[] mImages = new int[] { R.drawable.libin1,
R.drawable.libin2 };
#Override
public int getCount() {
return mImages.length;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == ((ImageView) object);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Context context = MainActivity.this;
ImageView imageView = new ImageView(context);
int padding = context.getResources().getDimensionPixelSize(
R.dimen.padding_medium);
imageView.setPadding(padding, padding, padding, padding);
imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
imageView.setImageResource(mImages[position]);
((ViewPager) container).addView(imageView, 0);
return imageView;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
((ViewPager) container).removeView((ImageView) object);
}
}
}
My question is how to get event listener if all the items in viewpager is finished.
private OnPageChangeListener mListener = new OnPageChangeListener() {
#Override
public void onPageSelected(int arg0) {
// TODO Auto-generated method stub
selectedIndex = arg0;
}
boolean callHappened;
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub
if( mPageEnd && arg0 == selectedIndex && !callHappened)
{
Log.d(getClass().getName(), "Okay");
mPageEnd = false;//To avoid multiple calls.
callHappened = true;
}else
{
mPageEnd = false;
}
}
#Override
public void onPageScrollStateChanged(int arg0) {
// TODO Auto-generated method stub
if(selectedIndex == adapter.getCount() - 1)
{
mPageEnd = true;
}
}
};
ViewPager.setOnPageChangeListener(mListener);
onPageScrolled or onPageSelected any of these you can use here and also check the selected page is equals to the number of items in the ViewPager.
these three callbacks work like this:
onPageScrollStateChanged is called with state ViewPager.SCROLL_STATE_DRAGGING
onPageScrolled will be called many times, its parameter positionOffset and positionOffsetPixels are keep increasing
onPageScrolled's parameter positionOffset more than 0.5, onPageScrollStateChanged is called with state SCROLL_STATE_SETTLING
onPageSelected is called with next page index as position
onPageScrolled will be called many times, its parameter positionOffset and positionOffsetPixels are keep increasing
onPageScrolled's parameter positionOffset is 1, onPageScrollStateChanged is called with state SCROLL_STATE_IDLE
The logic should not put in onPageSelected and onPageScrollStateChanged, because with them you only know state is changed. only in onPageScrolled you can get the direction. So my implementation is like this:
private int selectedPageIndex = -1;
private boolean exitWhenScrollNextPage = false;
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (exitWhenScrollNextPage && position == PAGE_COUNT - 1) {
exitWhenScrollNextPage = false; // avoid call more times
AndroidLog.error("-------- YEAH!");
}
}
#Override
public void onPageSelected(int position) {
selectedPageIndex = position;
}
#Override
public void onPageScrollStateChanged(int state) {
if (state == SCROLL_STATE_IDLE) {
exitWhenScrollNextPage = selectedPageIndex == PAGE_COUNT - 1;
}
}
Thanks Triode for great answer.
I used the code you posted for my project. In my case, I am finishing the activity if a user swipes to the right on last fragment. It worked fine, but it was still finishing the activity when you swipe to left instead of swiping right.
I made a change in your code and put adapter.getCount() - 1 in place of selectedIndex. In this case it will not finish the activity if user swipes to left.
if( mPageEnd && arg0 == adapter.getCount()-1 && !callHappened){
Log.d(getClass().getName(), "Okay");
mPageEnd = false;//To avoid multiple calls.
callHappened = true;
}else{
mPageEnd = false;
}
Suggested answers are complex than I thought.So I created a simple one.
I used OnPageChangeListener in ViewPager class. Here we have 3 methods.
void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
void onPageSelected(int position);
void onPageScrollStateChanged(int state);
It is important to know what are the states in onPageScrollStateChanged() and their sequence of execution.There are 3 states as SCROLL_STATE_IDLE, SCROLL_STATE_DRAGGING and SCROLL_STATE_SETTLING.
Here is the execution order.
Scroll_state_dragging --> Scroll_state_settling --> onPageSelected() --> Scroll_state_idle
The idea is keeping a flag inside onPageSelected() to record current page number. Then if user in the last page and swipe left scroll_state_dragging called and launch the next view.
private int pagePosition; // keep a class variable
private int[] layouts= new int[]{
R.layout.welcome_slide1,
R.layout.welcome_slide2,
R.layout.welcome_slide3};
ViewPager.OnPageChangeListener viewPagerPageChangeListener = new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
addBottomDots(position);
pagePosition = position;
}
#Override
public void onPageScrolled(int position, float positionOffset, int arg2) {
}
#Override
public void onPageScrollStateChanged(int state) {
if (state == ViewPager.SCROLL_STATE_DRAGGING) {
if (pagePosition == layouts.length - 1) {
launchHomeScreen();
}
}
}
};
private void launchHomeScreen() {
startActivity(new Intent(IntroductionActivity.this, MainDockerActivity.class));
finish();
}
I have created a complete example here.
#Override
public void onPageScrolled(int pageScrolledOn, float positionOffset, int positionOffsetPixels) {
if(positionOffset == 0F){
// meaning you cannot scroll further in current scroll direction
int lastPage = viewPagerAdapter.getCount - 1;
if (pageScrolledOn == lastPage){
//scroll on last page has occured
scrolledOnLastPage = true;
}
}
}
When you are at the last page and you slide to finish the activity the state SCROLL_STATE_SETTLING is skipped and the pager goes directly to SCROLL_STATE_IDLE
private val viewPagerPageChangeListener = object : ViewPager.OnPageChangeListener{
var blnPageTriesToSettle = false
override fun onPageScrollStateChanged(state: Int) {
when(state){
SCROLL_STATE_IDLE ->{
if (!blnPageTriesToSettle){
println("You are at the last page and you slide to finish")
finish()
}
}
SCROLL_STATE_DRAGGING ->{ blnPageTriesToSettle = false }
SCROLL_STATE_SETTLING ->{ blnPageTriesToSettle = true }
}
}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
}
override fun onPageSelected(position: Int) {
}
}