Viewpager no longer swipeable after onClick is set on inflated View - android

I have a viewpager, which contain an image. If I create the image view using an xml layout as below
#Override
public Object instantiateItem(final ViewGroup container, final int position) {
final int realPosition = getItemPosition(position);
View view = LayoutInflater.from(mContext).inflate(R.layout.item_adsong, container, false);
ImageView imageView = (ImageView)view.findViewById(R.id.adsong_img);
/... set to the image.../
view.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
onClickPage(realPosition);
}
});
container.addView(view);
return view;
}
After adding the onClickListener, I can no longer swipe my view pager from page to page. Without the onClickListener, I could do so.
However, if I create my ImageView from the code (instead of using xml layout) as below
#Override
public Object instantiateItem(final ViewGroup container, final int position) {
final int realPosition = getItemPosition(position);
ImageView view = new ImageView(mContext);
/... set to the image.../
view.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
onClickPage(realPosition);
}
});
container.addView(view);
return view;
}
I could now swipe the view pager, and I could do the click as I like. Why my first example above is intercepting the swipe, while the second is not? How could I fix my first example above to allow both swipe and onClick still work (I prefers using the layout, as I have several more e.g. some TextViews than just ImageView to show in my view pager). Thanks!

After further thorough debug and investigation, found the issue as per https://groups.google.com/forum/#!topic/android-developers/wIqNP4fzJQc. The cause of the issue is because the existence of TextView in my .xml layout. The TextView is scrollable by default.
Given the TextView is scrollable, the ViewPager then would allow the text to scroll by not intercepting the scrolling touch, and passing to the child. Hence the scrolling touch of the ViewPager is not working.
To fix the problem I override the canScrollHorizontal function of the TextView to return false. This solve the problem
public class MyTextView extends TextView {
//...
#Override
public boolean canScrollHorizontally(int direction) {
return false;
}
//....
Note: using setHorizontallyScrolling(false) or setting the android:scrollHorizontally="false" attribute in the XML of the TextView doesn't solve the problem somehow.
Hope this helps some who face the ViewPager no longer scrollable issue.

Related

PagerAdapter returns wrong position for Multi View Pager

I am implementing caurosel like view pager by adjusting page margin. I am able to implement like below
but i am facing problem with PagerAdapter
#Override
public Object instantiateItem(ViewGroup container, int position) {
View view= inflater.inflate(R.layout.mylayout, null);
//
Button btn=(Button)view.findViewById(R.id.button1);
btn.setTag(position);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.e("position",v.getTag()+"");
}
}
return imgView;
}
Though I am setting Tags, I am able to get correct position only for center page (Selected item), but when I click on left side page's button it returns wrong value.
I have searched about this issue lot, I could not find the solution so far.
looking for your help.
Thanks
You can use RecyclerView
LinearLayoutManager layoutManager = ...
recyclerView.setLayoutManager(layoutManager);
//when you want horizontal
layoutManager.setOrientation(context,LinearLayoutManager.HORIZONTAL,false);
//when you want vertical
layoutManager.setOrientation(context,LinearLayoutManager.VERTICAL,false);
There was no issues with PagerAdapter. Actual issue was due to a third party library which i used as wrapper to animate the views.

How to change an image in a ListView, when the image is clicked?

EDIT: I've solved this issue, if interested, please take a look at my answer to see how I did it!
I am currently working in Android Studio. I have a ListView that I populate with several items. Within each of these items is an ImageButton that has a "+" as the image. What I want to do is, whenever that image is clicked (not the entire ListView item, just the image), I want that image of "+" to become another image. Any help would be appreciated, as this has been an ongoing issue for a while!
Here is the current code that I attempt to use to achieve this:
final ImageButton movieSeen = (ImageButton convertView.findViewById(R.id.movieWatched);
movieSeen.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
movieSeen.setImageResource(R.drawable.ic_check_circle_black_24dp);
}
});
Currently this does update the image that I click correctly, BUT it also updates images that are not yet rendered on the screen, so when I scroll the list view down, other objects are also changed to ic_check_circle_black_24dp.
What I want is pretty straightforward, I just don't know how to achieve it. I just want to click an ImageButton, that's inside an item on a ListView, and have that ImageButton change its image resource.
Here is my custom array adapter as requested!
private class MovieScrollAdapter extends ArrayAdapter<Movie> {//custom array adapter
private Context context;
private List<Movie> movies;
public MovieScrollAdapter(Context context, List<Movie> movies){
super(context, -1, movies);
this.context = context;
this.movies = movies;
if(this.movies.isEmpty()){//if no results were returned after all processing, display a toast letting the user know
Toast.makeText(getApplicationContext(), R.string.no_matches, Toast.LENGTH_SHORT).show();
}
}
#Override
public View getView(final int position, View convertView, ViewGroup parent){
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) context.
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.movie_layout, parent, false);
}
TextView title = (TextView) convertView.findViewById(R.id.title);
title.setText(movies.get(position).getTitle());
TextView plot = (TextView) convertView.findViewById(R.id.plot);
plot.setText(movies.get(position).getPlot());
TextView genre = (TextView) convertView.findViewById(R.id.genre);
genre.setText(movies.get(position).getGenre());
TextView metaScore = (TextView) convertView.findViewById(R.id.metascore);
if(movies.get(position).getMetaScore() == -1){//if the metaScore is set to -1, that means movie has not been rated, which by inference means it is not yet released
metaScore.setText(R.string.movie_not_released);
metaScore.setTextSize(TypedValue.COMPLEX_UNIT_SP, 9.5f);//smaller text so it fits without breaking anything
metaScore.setTextColor(getColor(R.color.colorAccent));
} else {
metaScore.setText(" " + Integer.valueOf(movies.get(position).getMetaScore()).toString() + " ");//using white space for minor formatting, instead of altering margins each time this is rendered
metaScore.setTextSize(TypedValue.COMPLEX_UNIT_SP, 25);
//setting up a "highlighted" background to achieve metacritic square effect
Spannable spanText = Spannable.Factory.getInstance().newSpannable(metaScore.getText());
spanText.setSpan(new BackgroundColorSpan(getColor(R.color.metaScore)), 3, 7, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
metaScore.setText(spanText);
metaScore.setTextColor(getColor(android.R.color.primary_text_dark));
}
ImageView image = (ImageView) convertView.findViewById(R.id.imageView);
new ImageDownloadTask((ImageView)image).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, movies.get(position).getPosterURL());//because there are several images to load here, we let these threads run parallel
title.setOnClickListener(new View.OnClickListener() {//setting up a simple onClickListener that will open a link leading to more info about the movie in question!
#Override
public void onClick(View v) {
Uri uri = Uri.parse(movies.get(position).getMovieURL());
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
}
});
final ImageButton movieSeen = (ImageButton) convertView.findViewById(R.id.movieWatched);
movieSeen.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
movieSeen.setImageResource(R.drawable.ic_check_circle_black_24dp);
}
});
return convertView;
}
}
The problem is on a ListView, the views are being reused to save memory and avoid creating a lot of views, so when you change a view it keeps the state while it's being reused to show another item.
For example, you have 100 elements, you touch the first element ImageButton and that button is changed. Maybe on the screen there are 5 elements of the list showing, and you changed the first one. But if you scroll to the element number 15 the system is not creating 15 views, is taking the first one you clicked before and is changing the content.
So, you are expecting to have a view with a "+" ImageButton icon but you see another icon, that's because you must keep the view state inside a model object and set the state every time 'getView' is called.
Post your list adapter to see how is implemented.
UPDATE:
Now I see your adapter implementation I suggest you to add an int field inside Movie class to save the resource id you want to show on the ImageButton. Then inside the onClickListener you must set to this field the resource you want to show on the view when its clicked, and call notifyDataSetChanged(). After that you must do inside getView():
movieSeen.setImageResource(movies.get(position).getButtonImageResource());
Use RecyclerView and set the OnItemClickListener on your ImageButton within your view holder.
This already answered question should help.
The adapted code below is coming from this nice tutorial. Using ReciclerView with an adapter like this will solve your concern.
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private ArrayList<String> mDataset;
public class ViewHolder extends RecyclerView.ViewHolder {
public ImageView imageView;
public TextView txtHeader;
public ViewHolder(View v) {
super(v);
txtHeader = (TextView) v.findViewById(R.id.xxx);
imageView = (ImageView) v.findViewById(R.id.yyy);
}
}
public MyAdapter(ArrayList<String> myDataset) {
mDataset = myDataset;
}
#Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.rowlayout, parent, false);
ViewHolder vh = new ViewHolder(v);
return vh;
}
// Replace the contents of a view (invoked by the layout manager)
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
final String name = mDataset.get(position);
holder.txtHeader.setText(mDataset.get(position));
holder.imageView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// Do here what you need to change the image content
}
});
holder.itemView.setBackground(....); // Initialize your image content here...
}
//...
}
Here is my suggestion to achieve what you want :
Create An Interface in your adapter :
public interface YourInterface{
void selectedImage(int position,ImageView iamgeView);
}
Create variable interface in your adapter that you just created :
private YourInterface yourInterface;
and make your adapter constructor like this :
public YourAdapterConstructor(YourInterface yourInterface){
this.yourInterface = yourInterface;
}
in your ImageView onClickListener :
final ImageButton movieSeen = (ImageButton) convertView.findViewById(R.id.movieWatched);
movieSeen.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
yourInterface.selectedImage(position, imageView);
}
});
and then finally in your class activity, Implements YourInterface and change you ImageView there :
#Override
public void selectedImage(final int position,final ImageView imageView) {
//change your image view here
}
I'd like to thank everyone for their support. Unfortunately, with the way my code is written (rather messily and without much regard for what my professors taught me), I was unable to get most of these solutions to work. I did however, find a solution that falls in line with my own framework that I've had going into this. Unfortunately I could not redo my entire adapter method, or implement various interfaces that would cause me to have to rewrite a huge chunk of code for something seemingly trivial.
So, if anyone finds themselves in this situation in the future, here is my solution:
In the Movie class, I add a boolean value that represents my values, along with some getters and setters:
private boolean watchedStatus;
public boolean hasSeen() {return watchedStatus;}
public void toggleWatchedStatus(){
watchedStatus = !watchedStatus;
}
In the getView method, I simply get a reference to the ImageButton, and then based on the boolean value returned by "hasSeen," I set the ImageResource to one of two states:
#Override
public View getView(final int position, View convertView, ViewGroup parent){
ImageButton movieSeen = (ImageButton) convertView.findViewById(R.id.movieSeen);
if(movies.get(position).hasSeen()){
movieSeen.setImageResource(R.drawable.ic_check_circle_black_24dp);
} else {
movieSeen.setImageResource(R.drawable.ic_add_circle_black_24dp);
}
}
Next, I override the OnClickListener, and make it so that whenever the button is clicked, the boolean value in the Movie.java class is toggled. The key here was using the ArrayAdapter's method "notifyDataSetChanged()" This completes the process, and lets the ListView know that it should update itself:
final ImageButton movieSeenForClick = (ImageButton) convertView.findViewById(R.id.movieSeen);
movieSeen.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//movieSeenForClick.setImageResource(R.drawable.ic_check_circle_black_24dp);
movies.get(position).toggleWatchedStatus();
System.out.println(movies.get(position).hasSeen() + " ------- position: " + position);
notifyDataSetChanged();
}
});
Thanks again for the time taken to provide information, a lot of it really did steer me int he right direction, I just had to use the information correctly with the way my code was structured.

RecyclerView set views to be recycled

in my app, I'm implementing a recyclerview. My dataset for this recyclerview will have varying sizes according to the options that I set for the data to be displayed on the recyclerview.
One of the actions that I take with my recyclerview is to "expand" an item when a click is done on it, displaying further options in it. When pressing on this "expanded" item, I perform the action of "closing" it. Also, there can only be on "expanded" item at maximum at any moment.
The thing is that I understand that recyclerview recycles its row-views when they get out of sight for improved performance. However, because I am trying to have only one "expanded" item at a time, this recycling messes it up quite a lot.
What happens right now is that when I "expand", say the item related to position 1 of my dataset, as shown in the image below.
When I scroll down, I will see that the rowview for this item being recycled at a random chance since I will see this "expanded" view on items that I have not set to be "expanded", as shown in the image below.
And of course, when this happens, then when I scroll back to the item that I have selected to "expand", it will be "closed" as you would have expected.
So I have been thinking that I could resolve this problem by setting the possible number of views to be something like 80% of my dataset size will decrease the possibility of this problem occurring while still reduced, but enjoy some improved performance.
Another solution I thought about was disabling this "expanded" view from being recycled for other views and when this "expanded" item's position comes into screen, it gets bounded to this specific view. I thought of this solution after seeing that there is a concept of "scrap" and "recycle" for recyclerview, but I am not so sure if this method is even possible because I think I have only vaguely understood this side of recyclerview.
That being said, my question is are there ways for me to set the number of views to be recycled for a recycled view? Or even better, having one view from being recycled for items other than the "expanded" item?
Thanks in advance.
EDIT:
here's my (I know it's very messy I'm sorry...) code for my adapter:
public class DrinkMenuItem extends RecyclerView.Adapter<DrinkMenuItem.ViewHolder> {
private Context context;
private ViewGroup parent;
private ArrayList<Drink> menu;
private ArrayList<DrinkSelected> selected;
private DrinkMenuBasketItem selectedAdapter;
public int expanded = -1;
public boolean expandedVisible = false;
private DrinkMenuDropdownItem dropdownAdapter;
public static class ViewHolder extends RecyclerView.ViewHolder {
public RelativeLayout layout;
public TextView name, price;
public ListView dropdown;
public RelativeLayout basket;
public boolean tabbed = false;
public ViewHolder(View itemView) {
super(itemView);
layout = (RelativeLayout)itemView.findViewById(R.id.drink_menu_layout);
name = (TextView)itemView.findViewById(R.id.drink_menu_name);
price = (TextView)itemView.findViewById(R.id.drink_menu_price);
dropdown = (ListView)itemView.findViewById(R.id.drink_menu_dropdown_list);
basket = (RelativeLayout)itemView.findViewById(R.id.drink_menu_basket_button);
}
}
public DrinkMenuItem(Context context, ArrayList<Drink> menu, ArrayList<DrinkSelected> selected, DrinkMenuBasketItem selectedAdapter) {
this.context = context;
this.menu = menu;
this.selected = selected;
this.selectedAdapter = selectedAdapter;
this.dropdownAdapter = null;
}
public void updateDropdown(int requestedOption, int responsedOptionitem) {
dropdownAdapter.updateSelectedOption(requestedOption, responsedOptionitem);
notifyDataSetChanged();
}
// Create new views (invoked by the layout manager)
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
this.parent = parent;
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_drink_menu, parent, false);
return new ViewHolder(itemView);
}
// Replace the contents of a view (invoked by the layout manager)
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Drink d = menu.get(position);
holder.name.setText(d.getName());
holder.price.setText(d.getPrice() + d.totalAdditionalPrice() + "원");
if(position == expanded) {
//delete dropdown
holder.dropdown.setAdapter(null);
menu.get(position).returnToUnselected();
holder.price.setText(menu.get(position).getPrice() + "원");
setListViewHeight(holder.dropdown);
//reset dropdown-related stuff
holder.tabbed = false;
holder.basket.setVisibility(View.GONE);
}
setOnClickEvent(holder, position, parent);
}
private void setOnClickEvent(final ViewHolder holder, final int position, final ViewGroup parent) {
holder.layout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!holder.tabbed) {
//close dropdown of expanded view
if(expanded != -1) notifyItemChanged(expanded);
//make dropdown
dropdownAdapter = new DrinkMenuDropdownItem(context, menu, position, holder.price);
holder.dropdown.setAdapter(dropdownAdapter);
setListViewHeight(holder.dropdown);
//set dropdown-related stuff
holder.tabbed = true;
holder.basket.setVisibility(View.VISIBLE);
expanded = position;
expandedVisible = true;
((RecyclerView) parent).smoothScrollToPosition(position);
} else {
//delete dropdown
holder.dropdown.setAdapter(null);
menu.get(position).returnToUnselected();
holder.price.setText(menu.get(position).getPrice() + "원");
setListViewHeight(holder.dropdown);
expanded = -1;
expandedVisible = false;
//reset dropdown-related stuff
holder.tabbed = false;
holder.basket.setVisibility(View.GONE);
}
}
});
...
}
}
I have exactly same issue in my project. I did not succeed solving it with recyclerView. But the solution would be one of the following:
Create an expandableListView instead of recyclerView and everything will work great.
Create a ScrollView, and put a LinearLayout with android:orientation="vertical". Then, create a loop and insert all your custom views, and set click listener where you wish to expand.
Use an Expandable RecyclerView Library like one of these:
https://github.com/h6ah4i/android-advancedrecyclerview
https://github.com/bignerdranch/expandable-recycler-view

How to perform click event on View Pager in android

I need to show an image in full screen, on clicking of View Pager in android.
I have tried this.
view.myPager.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Log.e("VIEW PAGER", "VIEW PAGER");
Toast.makeText(activity, "ZOOM", Toast.LENGTH_SHORT).show();
}
});*
Suggestion appreciated.
Thanks
Set the listener on the image inside instantiateItem():
#Override
public Object instantiateItem(View collection, int position) {
final LayoutInflater inflater = getLayoutInflater();
View layout = inflater.inflate(R.layout.my_layout, null);
final ImageView image = (ImageView)layout.findViewById(R.id.image_display);
final int cPos = position;
image.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
ImageView i = (ImageView)v;
if(cPos == 0)
{
//...
}
//...
}
});
return layout;
}
Alternatively, you could use the ImageView.setTag() method to include data about what Activity to launch. e.g.
if(position == 0) image.setTag("com.blah.android.SomeActivity");
if(position == 1) image.setTag("com.blah.android.AnotherActivity");
//...
And the inside the onClick() above have this instead:
ImageView i = (ImageView)v;
String activityClassName = (String)i.getTag(); // Get the info we stored in the tag.
MyActivity.this.startActivity((new Intent()).setClassName(MyActivity.this, activityClassName));
Note that here you don't actually need the cast to ImageView, since getTag() is a method of View. You also don't need a separate OnClickListener for each ImageView. You could just create a single instance of an OnClickListener that grabs the tag data as above, and launches the appropriate activity. Set this OnClickListener on every ImageView inside instantiateItem().
P.S. I strongly recommend, if you are downloading images, that you look at some of the image downloaders that have been written for Android. e.g.
https://github.com/nostra13/Android-Universal-Image-Loader
You cannot click on a ViewPager, as a ViewPager manages a UI, but does not have its own UI.
You will need to add appropriate listeners (e.g., OnClickListener) to widgets inside pages in the ViewPager, just as you would for an app that did not have a ViewPager.

Dynamically Add and Remove Views from Layout

I'm working on one demo project in that I had create one XML file containing some views like ImageView, EditText. I'm loading this XML file on FrameLayout at runtime. At one point I want to remove all that views and again want to display them, I used removeView() method on button click but it does not work for me,,Please tell me the right way to do it..
public class Demo extends Fragment implements OnClickListener, OnTouchListener{
//Declaration of framelayout
FrameLayout f;
//Declaration of imageview
ImageView imageview;
View view, framelayoutview;
File file;
EditText etcardname, EditTextUserName,EditTextUsesrMobNumber,EditTextUsesrEmailID,EditTextUsesrAddress;
TextView dialogtesting;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.activity_modify_card ,container, false);
framelayoutview = inflater.inflate(R.layout.frame_layout_data ,container, false);
captureImageInitialization();
Initialize();
return view;
}
// Initialization of all views components
private void Initialize() {
f=(FrameLayout)view.findViewById(R.id.framelayout);
Button btneditcardreset=(Button)view.findViewById(R.id.buttonresetcard);
btneditcardreset.setOnClickListener(this);
Bundle bundle = this.getArguments();
int myInt;
myInt = bundle.getInt("position");
imageview=(ImageView)framelayoutview.findViewById(R.id.imageViewicon);
EditTextUserName=(EditText)framelayoutview.findViewById(R.id.modifycardeditTextusername);
EditTextUsesrMobNumber=(EditText)framelayoutview.findViewById(R.id.editTextmobilesnumber);
EditTextUsesrEmailID=(EditText)framelayoutview.findViewById(R.id.editTextemailid);
EditTextUsesrAddress=(EditText)framelayoutview.findViewById(R.id.editTextaddress);
imageview.setOnTouchListener(this);
EditTextUserName.setOnTouchListener(this);
EditTextUsesrMobNumber.setOnTouchListener(this);
EditTextUsesrEmailID.setOnTouchListener(this);
EditTextUsesrAddress.setOnTouchListener(this);
f.addView(framelayoutview);
}
#Override
public void onClick(View v){
if(v.getId()==R.id.buttonresetcard){
if(framelayoutview.getParent()!=null){
f.removeAllViews();
}
f.addView(framelayoutview);
}
}
Sorry to all I forgot to tell that I provided OnTouchListener so that I can move the views anywhere in Layout. So when I press reset button all Views should get move back to their original location means where they were at on first load..
you can use
imageview.setVisibility(View.GONE);
And
imageview.setVisibility(View.VISIBLE);
on particular event
If you had added your views dynamically to your FrameLayout then you can remove them.
Otherwise, if you showing your views from XML which are exist inside the frameLayout XML then you can't remove them but you can hide them from showing by setting setVisibility(View.GONE) or setVisibility(View.INVISIBLE).
Try this-
#Override
public void onClick(View v){
if(v.getId()==R.id.buttonresetcard){
if(framelayoutview.getParent()!=null){
f.setVisibility(View.GONE);
}
f.addView(framelayoutview);
}
}

Categories

Resources