Loading a view within a fragment on click? - android

I have a RelativeLayout with an Imageview inside of it. When I click the ImageView, I need to call an Async task to get some data and display it at the bottom of my screen in a RecyclerView. Like this:
Before clicking anything:
Click on ImageView --> (Calls Async task and displays progress bar):
Result:
I have the initial layout working (the first image) but I'm not sure how to reorganize my layout to make this whole thing happen on click. I would also need to be able to have the RecyclerView grow as I will be allowing the user to load more "comments" in that bottom section.
The simplified layout I have right now is basically this:
<!--Does all of this belong in a ScrollView to allow for the comments section at the bottom? -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- Lots of TextViews and buttons-->
<ImageView
android:id="#+id/main_picture_with_unknown_size"
android:layout_width="match_parent"
android:layout_height="wrap_content/>
<ImageView
android:id="#+id/button_comments"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/ic_comments" />
<!-- Do I need to put the comments section here in a FrameLayout?-->
</RelativeLayout>
I have a click listener on the imageview that I want to use as the trigger for the comments section. How do I expand/load the view on click?

You can do it like this:
Add a recycler view to your layout and hide it by android:visibility="gone".
Implement a RecyclerView Adapter and set it to your RecyclerView
When user clicks on the image, start your AsyncTask. After task finishes, get your list and give it to your adapter. Notify the adapter to refresh itself with new data by simply calling adapter.notifyDataSetChanged(); from your activity. Now you have a full recycler view. You can set recyclerView.setVisibility(true);
To implement infinite scroll, you can do something like this:
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (layoutManager.findLastVisibleItemPosition() == adapter.getItemCount() - 1) {
// LOAD MORE COMMENTS AND ADD GIVE THEM TO YOUR ADAPTER
}
}
});
Hope it makes sense.

Related

RecyclerView behind cardView is clickable when it should not be

I have a cardview, behind which there is a recyclerview of "Suggested Contacts", much like the default dialer in android. The CardView is placed to the bottom of the screen, and occupies about 70% of the screen, while the RecyclerView extends its height to match_parent. When the RecyclerView is scrolled, the CardView disappears and reveals the whole list.
The user can click on one of the RecyclerView's items to make a call directly. The issue is that items behind the CardView, which are not visible to the user, are also clickable.
How can I have the RecyclerView items which are visible only as clickable? (i.e. while the cardView is visible)
xml code:
<ScrollView
android:id="#+id/sv_suggested_contacts"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="expandSuggestions">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rv_suggested_contacts"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</ScrollView>
<androidx.cardview.widget.CardView
android:id="#+id/dialer_card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:cardElevation="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
[...]
I'm assuming your requirement is that the cardview should block all clicks, so that items below don't take the click right.
If that is the case and you are sure cardview is on top of the recyclerview you simply need to set an onClickListener on your cardview and do nothing inside it. Basically an empty click.
Or you can in xml for cardview add
android:onClick="nullClick"
and in the activity create the empty function
public void nullClick(View view) {
}
whichever way you like.
you can hide (setVisibility(View.GONE) item behind recycler, or set them not clickable

How to make recyclerView_items unclickable, while one of them is clicked and working?

Premise
Each of my RecyclerView_items displays an image, and changes its image_resource when tapped.
One of them is the right answer which has additional function: navigation to another fragment 5-second after changing its image.
Basically, the function of clickListener of RecyclerView_items is
wrong item
click -> change image_resource
right item
click -> change image_resource -> delay(5000) -> navigate to another fragment
Problem
Then, my problem is that after the right_item is tapped, other items are clickable during delay(5000).
I don't want them to change their images during delay(5000).
How to do it?! Thanks in advance.
You can achieve this by adding a flag at time when user click right image then make that variable false. As such after the delay before moving to the next fragment make it true again.
boolean flag = true;
then in your item click method
view.setOnClickListner {
if (flag) {
if (rightimageclicked) {
flag = false;
delay {
flag = true;
// Move to other fragment
}
} else {
// change the image for wrong click
}
}
}
The simple trick you can use is make a FrameLayout or View that overlaps on the RecyclerView. Set ists visibility to GONE. Then inside activity or fragment where you can access this FrameLayout or View which is overlapping, add an empty OnClickListener on this view.
*viewId*.setOnClickListener { }
Now set its visibility to VISIBLE when you call delay. When delay finished again set its visibility to GONE
This is what I understood, you want to lock the entire screen (make all items or buttons unclickable) when you are in delay(5000).
This is what I would do, give a CLICKABLE overlay view on top of recycler view and toggle its visibility. I used the same for locking the entire screen while making an API call.
view_overlay.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/rl_progress"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/transparent"
android:clickable="true"
android:focusable="true"
android:elevation="5dp"
android:visibility="invisible">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="#android:color/transparent"
android:indeterminateTint="#android:color/transparent" />
</RelativeLayout>
Obviously you can replace ProgressBar with just View to get a transaprent view.
To include it in your main view, give this at the bottom of your parent layout or below recycler view. Whichever you choose make sure you have the overlay view on top of recycler view.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
...
...
...
<include layout="#layout/view_overlay" />
</RelativeLayout>

Collapsing CardView Animation not working correctly

What I'm trying to do
I have a RecyclerView with many items that are basically some CardView.
Those cards have a supporting text in the middle of their bodies, which has the visibility set to GONE by default, and it's made VISIBLE when I click the arrow on the right of the card.
I'm trying to animate the card while the text is revealed and while it's collapsed.
The picture below shows the expanded card and the collapsed one:
The CardView layout (I've removed some parts for readability):
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardCornerRadius="3dp"
card_view:cardElevation="4dp"
card_view:cardUseCompatPadding="true"
android:id="#+id/root">
<LinearLayout
android:id="#+id/item_ll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="#dimen/activity_vertical_margin">
<!-- The header with the title and the item -->
<TextView
android:id="#+id/body_content"
style="#style/TextAppearance.AppCompat.Medium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:layout_marginBottom="8dp"
android:text="#string/about_page_description"
android:textColor="#color/secondaryText"
android:visibility="gone"/>
<!-- The divider, and the footer with the timestamp -->
</LinearLayout>
</android.support.v7.widget.CardView>
The problem
The animations is working when the card is expanding and revealing the body TextView, however, when I try to collapse it back, the cards below the animated one overlaps the first one.
Example:
What I've tried so far
I've already asked a similar question about this behavior here before, but that solution is not working for a TextView in the middle of the card.
The code that's responsible for the animation part is inside the RecyclerView adapter. The arrow has a click listener that calls the method below:
private fun toggleVisibility() {
if (bodyContent.visibility == View.GONE || bodyContent.visibility == View.INVISIBLE) {
btSeeMore.animate().rotation(180f).start()
TransitionManager.beginDelayedTransition(root, AutoTransition())
bodyContent.visibility = View.VISIBLE
}
else {
btSeeMore.animate().rotation(0f).start()
TransitionManager.beginDelayedTransition(root, AutoTransition())
bodyContent.visibility = View.GONE
}
}
Where root is my CardView.
I've also tried to use the LinearLayout instead of the card itself for the delayed transition, but that didn't work either.
How can I achieve that behavior for my layout?
You will have to perform the transition on the RecyclerView, not on individual items. Otherwise, the RecyclerView layout changes aren't taken into account by the auto transition, because it will only look at what changes in that very child view, even though in fact, other ViewHolders are indirectly affected (layout parameters are changing).
So, instead of passing "root" (the item view) to TransitionManager#beginDelayedTransition, pass a reference to your RecyclerView
You have to apply TransitionManager.beginDelayedTransition on the root view where the cardview is contained
You have to remove android:animateLayoutChanges="true" from all over the layout
TransitionManager.beginDelayedTransition(the_root_view_where_card_view_exist, new AutoTransition());
RecyclerView does behave oddly if his items are resizing outside RecyclerViews callbacks. Try using adapter.notifyItemChanged(position, payload) and updating the item then:
Replace adapter's onclick with this:
adapter.notifyItemChanged(adapterPosition, true) // needs adapter reference, can use more meaningful payload
Then inside of your adapter:
override fun onBindViewHolder(holder: Holder, position: Int, payloads: List<Any>) {
if (payloads.isEmpty())
onBindViewHolder(holder, position)
else
holder.toggleVisibility()
}
You can also see what happens when running delayedTransition on LinearLayout instead of Card itself.
This won't be perfect, but it will trigger animation of following items instead of them jumping and clipping.
I would recomment you to use Animator framework and apply height animation to your TextView.
Here is a nice library you can use: https://github.com/cachapa/ExpandableLayout
I also suggest you to check it's source code, it uses the Animators
Maybe this is too late.
Inside onBindViewHolder() include this
holder.view.btSeeMore.setOnClickListener { view ->
val seeMore = (bodyContent.visibility != View.VISIBLE)
view.animate().rotation(if (seeMore) 180f else 0f).start()
bodyContent.visibility = if (seeMore) View.VISIBLE else View.GONE
}

How do I get SwipeRefreshLayout to not trigger before I lift finger, and show a "swipe to refresh text"

I have implemented a SwipeRefreshLayout on my ListView, and it is working, but not how I want it to.
First of all, the refresh triggers before I lift my finger, which doesn't feel right because it moves the content back to the top even though my finger is still sitting in swiped down position. Also the distance to trigger is really short and setDistanceToTriggerSync from the docs is not available to me for some reason.
second, I'd like some sort of view the be displayed in the gap when my list view is pulled down, like a text that tells the user "swipe down to refresh" or a an animation (like the dancing ghost in snap chat). How do I set this View
Here's what I have
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/dashboardRootLayout"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<ListView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/lvDashboard"
android:background="#ffffff"
/>
Why isn't the setDistanceToTriggerSync available?
Anyway the default behavior of the SwipeRefreshLayout does not have that extra view, it just has a special progress bar and a listener for refreshing and showing or not the progress of refresh. Also it can change the look of the actionbar with the refresh message.
Possible solution:
If you want that special view, of the top of my head I can think of having the list being moved down with animation and creating or putting VISIBLE the view you want to show on the top of the SwipeRefreshLayout.
SwipeRefreshLayout swipeRefreshLayout=(SwipeRefreshLayout)findViewById(R.id.dashboardRootLayout);
swipeRefreshLayout.setOnRefreshListener(new OnRefreshListener()
{
#Override
public void onRefresh()
{
// do animation
// set the view you want to show VISIBLE
}
});
You could actually implement all of this by yourself without the SwipeRefreshLayout since you don't want the default behavior.
}

ProgressBar at the bottom of gridview

I have a GridView which shows a grid of 15 Views for the first time. Then, on click of a button, I add 5 more grid views. So, my question is: How to add a ProgressBar at the bottom of a page, when those 5 Views are loading ? Like a spinner loading and the 5 Views get updated.
Hi i have a tab host tabwidget for tabs and gridview inside framlayout..now if i want the progressbar then should i need to inclue this linearlayout after frame layout or inside frame laoyout?
Add this below your GridView in the XML.
<LinearLayout
android:id="#+id/linlaProgressBar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal" >
<ProgressBar
style="#style/Spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="2dp" />
</LinearLayout>
And in your Activity, where you are loading the data (assuming, it is an AsyncTask), in your onPreExecute() show it:
#Override
protected void onPreExecute() {
// SHOW THE BOTTOM PROGRESS BAR (SPINNER) WHILE LOADING MORE PHOTOS
linlaProgressBar.setVisibility(View.VISIBLE);
}
And in the onPostExecute(), hide it:
#Override
protected void onPostExecute() {
// SHOW THE BOTTOM PROGRESS BAR (SPINNER) WHILE LOADING MORE PHOTOS
linlaProgressBar.setVisibility(View.GONE);
}
If you are not using an AsyncTask, then set the visibility to View.VISIBLE at the start of the method where you start downloading the data and set it to View.GONE either after or just before you set the Adapter.
EDIT: Adding additional info.
Couple of things.
You are downloading data off the Internet for which, I would recommend switching to AsycnTask instead of using a conventional () Method.
Check out my answer a few days ago on a similar question here: https://stackoverflow.com/a/13265776/450534
In that answer, you will find a complete solution that will suit your exact needs. Well, almost entirely anyway. You may have to make a few modifications and adapt to a few things yourself. But by and large, it will answer all your questions. I use it in my apps and they function as you say, the Google Play loading text at the bottom. And it really is complete. :-)
You need to create custom view for that and inflate it and add as bottom view in gridview
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<Button android:text="Load"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
/>
<ProgressBar
android:layout_width="25.0dip"
android:layout_height="25.0dip"
android:layout_centerInParent="true"
android:visibility="gone"
/>
</RelativeLayout>
now inflate this view and show the progressbar at bottom of gridview
You can set button as bottom with this when you click just visible the progressbar
Just add a ProgressBar in your layout, set its visibility to VISIBLE or GONE whenever you want to show/hide it.
Add a in the xml of GridView and set its property "alignParentRight = true" and "visibility = invisible" . In your activity , use Async task class and on its preExectue method set set its visibility to visible
pb.setVisibility(View.VISIBLE);
In doInBackground method load your images and finally in onPostExecute method make this progress bar invisible.
pb.setVisibility(View.INVISIBLE);
execute this async task class on click of load button.
This is an easy tutorial for understanding asyncTask
http://androidresearch.wordpress.com/2012/03/17/understanding-asynctask-once-and-forever/
Or threads can be used instead of async task.

Categories

Resources