Get clicked Image properties - java

Imagine a gallery application (sort of).
But instead of gallery I want to present a choice in form of 10 images displayed onto the screen.
How should you detect the one that user has clicked on?
What is a best way to implement this? Should I use ImageView and onClick method?
Imagine implementing onClick event for a 100 ImageViews?
?for every ImageView displayed onto the screen check if it contains user touch coordinates?
Same question bothers me for how to detect if the user has touched a Bitmap drawn onto a canvas.
Java, Android.

You're going to want to use a RecyclerView with an ImageView in your list item's layout xml.
You can create a clickable ViewHolder like this:
class ClickableViewHolder(final override val containerView: View, onClick: (position: Int) -> Unit) : RecyclerView.ViewHolder(containerView) {
init {
containerView.setOnClickListener {
val pos = absoluteAdapterPosition
// check if item still exists
if (pos != RecyclerView.NO_POSITION) {
onClick(pos)
}
}
}
}
Usage in your Adapter class:
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)
return ClickableViewHolder(v) { position: Int ->
getItem(position)?.let {
//Do something here
}
}
}

Related

How to change single item in recycler view without screen reload?

I have Recycler View with a lot of items in. What I want to do is to change the text in TextView inside item that was clicked. I did it in that way:
wordList.set(position, newWord);
MyProgressActivityAdapter newAdapter = new MyProgressActivityAdapter(wordList, this);
newAdapter.notifyItemChanged(position);
recyclerView.setAdapter(newAdapter);
And everything works fine except of the fact that the screen goes to the top every time I click item. What can I do to avoid that?
You should use the payload version of notifyItemChanged, here is a simple example for you to get the hang of it:
adapter.notifyItemChanged(position, "updateText");
And then in your RecyclerAdapter override the payload version of onBindViewHolder:
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position, #NonNull List payloads) {
if (payloads.isEmpty()) onBindViewHolder(holder, position);
else if ("updateText".equals(payloads.get(0))) {
if (holder instanceof YourViewHolder) {
((YourViewHolder) holder).textView.setText(dataProvider.get(position).getNewText());
}
}
}
Note that this approach prevents RecyclerView from creating a new ViewHolder and then binding your data, so you should just call the notifyItemChanged without resetting the adapter and so.
notifyItemChanged(position) should work if you handle it correctly. Try to handle this inside onBindViewHolder like below:
override fun onBindViewHolder(holder: RecyclerHolder, position: Int) {
holder.itemView.text_view.text = items[position]
holder.itemView.button.setOnClickListener {
items[position] = "New Text"
notifyItemChanged(position)
}
}

ViewPage swipe is hard in some android devices

I Have a view pager for image slider in my activity
I have a problem when I try to swipe to another page in some android device like xiaomim, note 8 devices, the movement of the viewPager became heavy
this is my code :
override fun isViewFromObject(view: View, p1: Any): Boolean {
return view == p1
}
//
override fun instantiateItem(container: ViewGroup, position: Int): Any {
val itemView: View = LayoutInflater.from(container.context).inflate(R.layout.image_slider_view, container, false)
val mSliderImage: ImageView = itemView.findViewById(R.id.slider_image)
Glide.with(itemView).load(images[position]).into(mSliderImage)
itemView.setOnClickListener {
if (clickable) {
val intent = Intent(itemView.context, ImageSliderActivity::class.java)
intent.putExtra("slider", images)
intent.putExtra("position", position)
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
val activityOptions = ActivityOptions.makeSceneTransitionAnimation(activity)
itemView.context.startActivity(intent, activityOptions.toBundle())
} else {
itemView.context.startActivity(intent)
}
}
}
container.addView(itemView)
Util.rotateViewsIfRTL(itemView)
return itemView
}
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
(container as ViewPager).removeView(`object` as View)
}
override fun getCount(): Int {
return images.count()
}
i don't know how many image you have , and what their sizes , i once faced this problem with image in viewpager .
In my case I have more then 10 images and each image have weight more 800kb, and I check this issue, and it really works fine if i change OffscreenPageLimit to 1 page and i tried to reduce the size of the image !
viewPager.setOffscreenPageLimit(1);
Try the following,
First instead of images, load any random string or integer and test if it still scrolls heavily. If it works fine then it means the images are heavy to load.
If images are heavy, check the following:
Try to WEBP format for images and load them. They are very effective. If that is not possible make sure images are at least .png format instead of .JPG
More efficient way
Use horizontal recycler view.
Here is the tutorial: https://demonuts.com/android-horizontal-recyclerview/

Recyclerview : how to mix some post with image and no image? [duplicate]

This question already has answers here:
How to create RecyclerView with multiple view types
(23 answers)
Closed 3 years ago.
I want to display in same recycle-view which is some post have image and some post does not have images.
I can retrieve all the post with image and non-image,
but i want to change the size of the post when the user only post text(no image).
I expect the output like twitter feed..some post with image and without image have their own size.
Simple way to achieve this scenario is, All you have to do is create a view with both image and text, in recycler adapter check if image data is available make visibility of image visible else Image visibility gone.
Second Approach for this to make multiple view for RecyclerView.
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Log.d(TAG, "onBindViewHolder called");
ContentItem item = mContentItems.get(position);
if(item.getName()!=null){
holder.textName.setVisibility(View.Visible);
holder.textName.setText(item.getName());
}else{
holder.textName.setVisibility(View.GONE);
}
if(item.getPreviewImageDefault()!=null){
holder.imageIcon.setVisibility(View.Visible)
Picasso.with(mContext).load("file://" + item.getPreviewImageDefault()).into(holder.imageIcon);
}else{
holder.imageIcon.setVisibility(View.GONE)
}
}
Another possible solution is create 2 xml layouts and use ViewType in your RecyclerView.
look this How to create RecyclerView with multiple view type?
If you want to hide the image when it is ic_launcher you could do that (suppposing that data.getImage() returns the id of the drawable as integer):
#Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
if(mItems!=null){
AdapterData data = mItems.get(i);
viewHolder.text.setText(data.getText());
viewHolder.image.setImageResource(data.getImage());
if(TextUtils.isEmpty(data.getText())){
viewHolder.text.setVisibility(View.GONE);
}else{
viewHolder.text.setVisibility(View.VISIBLE);
}
if(data.getImage()==R.drawable.ic_launcher){
viewHolder.image.setVisibility(View.GONE);
}else{
viewHolder.image.setVisibility(View.VISIBLE);
}
}
}
One possible solution, like some people have already said, is to hide/show the ImageView.
You could do that in the ViewHolder that you use for your RecyclerView.
class OptionalImageViewHolder extends RecyclerView.ViewHolder {
private ImageView image;
private TextView text;
// any other views you have
public OptionalImageViewHolder(View itemView) {
super(itemView);
image = itemView.findViewById(R.id.yourImageViewIdHere);
text = itemView.findViewById(R.id.yourTextViewIdHere);
// same for any other views you have
}
public void bindView(Tweet tweet) {
// This is where the magic happens
// Note: I make the assumption that you have a class called "Tweet"
// that has a field for "text", a field for "image" (that can be
// null if there's no image), and any other necessary fields.
text.setText(tweet.getTweetText());
if (tweet.hasImage() /* function that returns whether or not there is an image */) {
image.setVisibility(View.VISIBLE);
image.setImageBitmap(tweet.getImage()); // or however you are setting the image
} else {
// else just make the image invisible
image.setVisibility(View.GONE);
}
}
}
Hopefully this gives you an idea.
RecyclerView supports different viewTypes (layouts) which is the proper way in such scenario. E.g.,
class MyAdapter : RecyclerView.Adapter<MyViewHolder>() {
override fun getViewTypes(position:Int) =
if (mydata[position].hasImage) return R.layout.mylayout_with_image
else R.layout.mylayout_no_image;
override fun onCreateViewHolder(viewType:Int, parent:ViewGroup) : MyViewHolder =
// here viewType = layout id
MyViewHolder(layoutInflater.inflate(viewType, parent))
override fun onBindViewHolder(viewHolder:MyViewHolder, position:Int) {
// guaranteed viewHolder.itemView is the view you want for that position
}
}

Keep RecylerView adapter checkbox checked if user come back to the activity by clicking on same button[Kotlin]

I have activity one where having a empty textview user has click on this textview to select location from list of location so for that when user click on select location textview it will open list of location with checkbox.
When user select location(can select multiple location) and click on done then all selected location will be showing on activity one textView with all selected checked textview value now when user click on same textview to add more location then on recylerview list all previous checked item should be checked. I'm not getting all previous selected checkbox.
I'm not getting how to achieve this. I need all old checkbox should be selected and user can select some more new checkbox if click on same textview. Please help me to get this. Java code will be also helpful for me
Below is my recylerView Adapter code:-
class SelectMedicineAdapter (val medicineList : ArrayList<String>, val context: Context) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
var selectedCheckBoxMedicineList : ArrayList<String> = ArrayList()
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
holder.itemView.textViewSelectMedicineName.text = medicineList.get(position)
holder.itemView.checkboxSelectMedicine.setOnCheckedChangeListener { buttonView, isChecked ->
val itemText = medicineList.get(position)
if (isChecked) {
selectedCheckBoxMedicineList.add(itemText)
} else {
selectedCheckBoxMedicineList.remove(itemText)
}
}
}
fun getSelectedMedicineList(): ArrayList<String> {
return selectedCheckBoxMedicineList
}
override fun getItemCount(): Int {
return medicineList.size
}
override fun onCreateViewHolder(holder: ViewGroup, p1: Int): RecyclerView.ViewHolder {
val v= (LayoutInflater.from(context).inflate(R.layout.row_select_medicine_adapter,holder,false))
return ViewHolder(v)
}
class ViewHolder (itemView: View): RecyclerView.ViewHolder(itemView){
var textViewSelectMedicineName = itemView.textViewSelectMedicineName
var imageViewPlusButton = itemView.imageViewPlusButton
var imageViewMinusButton = itemView.imageViewMinusButton
var checkboxSelectMedicine = itemView.checkboxSelectMedicine
}
}
You need to update views with their items state (selected or not).
In onSaveInstanceState of your activity/fragment where your adapter is you should write adapters state (which items are selected (getSelectedMedicineList)) to the bundle.
Whenever your fragment/activity is restored just update adapter with data you saved restoreSelectedMedicineList(selectedCheckBoxMedicineList: ArrayList<String>)
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
holder.itemView.textViewSelectMedicineName.text = medicineList.get(position)
holder.itemView.checkboxSelectMedicine.setOnCheckedChangeListener(null)
if(selectedCheckBoxMedicineList.contains(itemText)) {
holder.itemView.setChecked(true)
} else {
holder.itemView.setChecked(false)
}
holder.itemView.checkboxSelectMedicine.setOnCheckedChangeListener { buttonView, isChecked ->
val itemText = medicineList.get(position)
if (isChecked) {
selectedCheckBoxMedicineList.add(itemText)
} else {
selectedCheckBoxMedicineList.remove(itemText)
}
}
}
fun restoreSelectedMedicineList(selectedCheckBoxMedicineList: ArrayList<String>) {
this.selectedCheckBoxMedicineList = selectedCheckBoxMedicineList
notifyDataSetInvalidated()
}
When you start an Activity, it has no knowledge of what is the state of your data. You need to provide the state for the Activity.
By which I mean when the user selects his/her desired locations and goes back to Activity One you to need hold on to these selected locations and when the user again wants to update the locations you need to pass these previously selected locations to the second Activity and then update the RecyclerView's backing data accordingly.
in your list you do maintain a boolean field when select check box then selected position value is true, and notify data and inside onbind viewholder you check first which position is true. if true then show selected checkbox otherwise unselect.
When I'm working with an app, I have faced same problem and also I needed that checked boxes even app closes. So I used SharedPreferences to stored and retrieve values that will indicates states of check boxes then I can easily specify the states of all element. So if you face same problem and have no solution you can use this way.
Just update the model class with a flag on every tick and untick. Check this flag to tick and untick logic for restoring the checkbox state.

How to make the hide and show animation of custom view

Task:
I wanna hide and show my view with animation.
Troubles:
First of all I tried to make it with changing view's visible (GONE, VISIBLE) (I use Transition API) but there were bugs with animation.
Then I tried to do it with scale animation but there was a bug with a hint in editText (child view).
My custom view which I wanna animate:
class SearchAdditionalView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
private val transitionSet: TransitionSet by lazy {
val transitionSet = TransitionSet()
with(transitionSet) {
addTransition(ChangeBounds())
interpolator = DecelerateInterpolator()
duration = 200
}
transitionSet
}
init {
inflate(context, R.layout.include_search_navbar_addition, this)
}
fun toggle() {
visibility = View.VISIBLE
TransitionManager.beginDelayedTransition(parent as ViewGroup, transitionSet)
}
}
Question:
Are there good practices for this animation and how can I integrate it?
TransitionManager.beginDelayedTransition() should be called first before making any changes to the ViewGroup. Check out the training docs which should give you an idea.

Categories