I'm looking for a solution to this that will allow me to expand a cardview to see more information and then easily collapse it. Google Keep has examples of cards like this. Anyone know how they do this? Would I create 2 versions of my cardview (one collapsed and one expanded) and then use an Animator class coupled with gesture methods to transition between the two views? I'm using a Recyclerview to hold my cardviews.
I found this if it is at all relevant: http://developer.android.com/training/animation/layout.html
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:orientation="vertical"
android:animateLayoutChanges="true"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
//here put the view which is always visible
<LinearLayout
android:layout_width="match_parent"
android:visibilty="gone"
android:id="#+id/expandableLayout"
android:layout_height="wrap_content">
//here put which will collapse and expand
</LinearLayout>
</android.support.v7.widget.CardView>
take a boolean isexpanded in you arraylist object class
if (listobj.isexpanded)
{
holder.expandableLayout.setVisibility(View.VISIBLE);
}
else {
holder.expandableLayout.setVisibility(View.GONE);
}
holder.cardView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (listobj.isexpanded)
{
holder.expandableLayout.setVisibility(View.GONE);
listobj.isexpanded=false;
notifyItemChanged(position);
}
else {
holder.expandableLayout.setVisibility(View.VISIBLE);
listobj.isexpanded=true;
notifyItemChanged(position);
}
}
});
try someting like this
Related
I have a RecyclerView with a CardView as the root element of the row item layout. I managed to have a ripple effect to CardView using below list item layout as described here.
Row item layout:
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="#dimen/margin_8dp"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true"
android:foreground="?android:attr/selectableItemBackground"
app:cardBackgroundColor="#color/cyan"
app:cardCornerRadius="20dp"
app:cardElevation="5dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/transparent"
android:padding="#dimen/margin_8dp">
<!-- Underlying views-->
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
The problem is that to have a click action on a row item in the RecyclerView adapter requires a couple of taps, the first tap for selecting the item and showing the ripple effect, and the second tap for the actual click to trigger View.OnClickListener.
Using android:focusableInTouchMode = "true" attribute in CardView is the reason for the double tap; but disabling it, also disable the ripple effect consequently.
I tried to use a custom Ripple effect as in this answer, but doesn't work. Also tried to have the ripple effect on the underlying CardView root element as in this answer, but still nothing new.
Similar thing for using the button style as in here.
And I have a transnational itemView click listener in RecyclerView adapter within the view holder
class ViewHolder extends RecyclerView.ViewHolder {
ViewHolder(#NonNull final View itemView) {
super(itemView);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
}
}
Replaced View.OnClickListener with View.OnFocusChangeListener, and when the row item gets a focus after it's been clicked, I run the same code as in onClick callback.
But that doesn't give the ripple effect a chance to show up, so posted that with some delay.
itemView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus)
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// Do whatever done in onClick()
}
}, 10); // delay to give the ripple effect a chance to show
}
});
I wish if there's another solution without any delay.
I solved this by totally removing android:focusableInTouchMode = "true" & android:foreground from the CardView, and adding the ripple effect to the root item within the CardView with android:background="?android:attr/selectableItemBackground"
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="#dimen/margin_8dp"
app:cardBackgroundColor="#color/cyan"
app:cardCornerRadius="20dp"
app:cardElevation="5dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:padding="#dimen/margin_8dp">
<!-- Underlying views-->
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
I'm looking for a component like this:
If you have used Tinder, i want something like when you view a profile, how you can cycle through their pictures.
I'm pretty sure i can implement this manually, but was wondering if something already exists, and i don't really know how to look it up.
Thanks!
Edit: Also sorry for the bad title, didn't really know how to name these types of questions.
You can do your own implementation or could use some libraries. For you own implementation I would suggest using either ViewPager passing Views instead of fragments or PageTransformer if you want something more elaborate.
If you prefer libraries, I would recommend InfiniteCycleViewPager, sayyam's carouselview or you can go in a tour here: https://android-arsenal.com/tag/154, there is a lot of libraries with different implementations.
Example of implementation of an image slider using ViewPager:
First create your activity's xml with a ViewPager component:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.stackoverflow.imageslider.MainActivity">
</android.support.v4.view.ViewPager>
Then in your activity's onCreate method instantiate the ViewPager and create an adapter for it:
ViewPager viewPager = findViewById(R.id.view_pager);
ViewPagerAdapter adapter = new ViewPagerAdapter(this, imageList);
viewPager.setAdapter(adapter);
In the ViewPagerAdapter class (that should extend PageAdapter), you will control the images overriding the method instantiateItem():
#NonNull
#Override
public Object instantiateItem(#NonNull ViewGroup container, int position) {
ImageView imageView = new ImageView(context);
imageView.setImageDrawable(imageList.get(position));
return imageView;
}
In this example imageList would be an List that is fulfilled somewhere else.
This example is based in a tutorial from codinginflow.com, and you can also take a look there.
Now let's see a simpler implementation, that would do just like you asked, touching the image sides instead of sliding.
Example of simpler implementation:
Create a layout with an ImageView and two buttons overriding it, one for next image on the right and one for previous image in the left:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:srcCompat="#tools:sample/backgrounds/scenic" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<Button
android:id="#+id/buttonPrevious"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#android:color/transparent" />
<Button
android:id="#+id/buttonNext"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#android:color/transparent" />
</LinearLayout>
</FrameLayout>
Then set the onClick of each button to get the images from a list and set in the ImageView.
final ImageView imageView = findViewById(R.id.imageView);
Button next = findViewById(R.id.buttonNext);
next.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
currentPosition ++;
imageView.setImageDrawable(drawableList.get(currentPosition));
}
});
Button previous = findViewById(R.id.buttonPrevious);
previous.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
currentPosition --;
imageView.setImageDrawable(drawableList.get(currentPosition));
}
});
drawableList would be a Drawable that is fulfilled somewhere else. You could also change to a different kind of image list, doesn't matter. current position would be and int that starts at 0.
For PageTransformer I would recommend Bincy Baby's answer and phantomraa's answer.
In case the link gets broken I'll leave Bincy Baby's code here:
viewPagerMusicCategory.setPageTransformer(false, new ViewPager.PageTransformer() {
#Override
public void transformPage(View page, float position) {
Log.e("pos",new Gson().toJson(position));
if (position < -1) {
page.setScaleY(0.7f);
page.setAlpha(1);
} else if (position <= 1) {
float scaleFactor = Math.max(0.7f, 1 - Math.abs(position - 0.14285715f));
page.setScaleX(scaleFactor);
Log.e("scale",new Gson().toJson(scaleFactor));
page.setScaleY(scaleFactor);
page.setAlpha(scaleFactor);
} else {
page.setScaleY(0.7f);
page.setAlpha(1);
}
}
}
);
I think you could also mix PageTransformer with the examples I gave.
The libraries each one already have a good documentation, if not in the android arsenal you can find it in GitHub, and even if I post some code here, if the library closes, gets outdated or something like that, the code will not be useful anymore.
I am using RecyclerView to view my data, but when there are many items ,RecyclerView will auto scroll to the bottom everytime a new item is inserted. how to prevent it ?
this is the insert code:
#Override
public void onChildAdded(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
getOneMessage(dataSnapshot.getKey(), new OneMessageCallBack() {
#Override
public void OnCallBack(Object_Message message) {
Object_Conversation conversation=new Object_Conversation(dataSnapshot.getKey(),message);
mConvers_List.add(conversation);
mConvers_Adapter.notifyItemInserted(mConvers_List.size()-1);
}
});
}
this the the XML :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<android.support.v7.widget.RecyclerView
android:id="#+id/rv_conversations"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
android:background="#color/white">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
Try using
android:descendantFocusability="blocksDescendants"
in recyclerview. This will help you avoid autoscroll
I think in order to disable the autoscroll you need to use notifyItemRangeInserted instead of notifyItemInserted. This should not make your RV scroll to the bottom.
final int positionStart = mConvers_List.size() + 1;
mConvers_List.add(conversation);
notifyItemRangeInserted(positionStart, mConvers_List.size());
You can use scrollToPosition(int position) method to scroll back to zero position.
RecyclerView.scrollToPosition(0)
I have an activity layout like this:
<?xml version="1.0" encoding="utf-8"?>
<SlidingPaneLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/course_menu_sliding_layout"
android:layout_width="match_parent"
android:background="#color/white"
android:layout_height="match_parent">
<!-- Here is any view that will represent your side menu. Don't forget to provide width! -->
<FrameLayout
android:id="#+id/course_activity_menu"
android:layout_width="#dimen/edu5_menu_width"
android:layout_height="match_parent"
android:background="#color/white"
android:layout_gravity="start" />
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/courseActivityNavigationController"
android:layout_width="match_parent"
android:layout_height="#dimen/edu5_navigation_controller_height"
/>
<FrameLayout
android:id="#+id/courseActivityContentFragmentContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/c_popup_bg"
android:layout_below="#id/courseActivityNavigationController"
/>
</RelativeLayout>
</SlidingPaneLayout>
Now, on the 'FrameLayout' with id: 'courseActivityContentFragmentContainer' I add a 'Fragment' which contains several 'Fragment's in it, one of them contains a 'ListView' inside its view layout.
The issue is that when I scroll the list, it begins to scroll but then the 'SlidingPaneLayout' takes control and slides the pane layout.
Any thoughts?
The solution I found was extend the 'SlidingPaneLayout' and to pass the visible rectangle of the of the 'ListView' to it so it won't intercept touch events that inside the 'ListView'
Here's is the code:
public class MySlidingPaneLayout extends SlidingPaneLayout {
private Rect mIgnoreRect;
...
...
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if ( !isOpen() && mIgnoreRect != null && mIgnoreRect.contains((int)ev.getX(), (int)ev.getY()) )
{
return false;
}
try
{
return super.onInterceptTouchEvent(ev);
}
catch ( Exception ex )
{
Log.e(TAG, "super.onInterceptTouchEvent threw exception", ex);
}
return false;
}
}
By the way, I do a try and catch because sometimes there's a 'NullPointerException' thrown, I couldn't find out why.
AndroidSlidingUpPanel
Here, I am using slidingup panel library. You can see both the panel and listview in the following screen. What i am trying to do is to hide the panel if i click outside the panel(Dim Area). Instead it is clicking on the listview in the above layout. How can i achieve this?
Here is the XML,
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.voucher.point.activity"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<!-- sothree:dragView="#+id/dragView" -->
<com.sothree.slidinguppanel.SlidingUpPanelLayout
xmlns:sothree="http://schemas.android.com/apk/res-auto"
android:id="#+id/sliding_layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="bottom"
app:fadeColor="#color/transparent"
sothree:panelHeight="40dip"
sothree:paralaxOffset="200dp" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#color/white"
android:orientation="vertical" >
<ListView
android:id="#+id/offersList"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scrollbars="none"
android:visibility="gone" >
</ListView>
</LinearLayout>
<include
android:id="#+id/bottom_menu"
android:layout_width="fill_parent"
android:layout_height="270dip"
layout="#layout/side_menu" />
</com.sothree.slidinguppanel.SlidingUpPanelLayout>
Can i do like this?
With the version 3.3.0 and later, it's possible to do that in the following way
final SlidingUpPanelLayout slidingUpPanelLayout = (SlidingUpPanelLayout) findViewById(R.id.sliding_layout);
slidingUpPanelLayout.setFadeOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
slidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
}
});
source
I believe you have fixed your problem but for others who has same requirement I added another View on top of my map (in xml layout) and set it's touch click-ability.
So my xml file is like this:
<com.sothree.slidinguppanel.SlidingUpPanelLayout
android:id="#+id/sliding_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#+id/booking_confirm_layout"
android:gravity="bottom"
app:umanoFadeColor="#color/black_75_percent"
app:umanoOverlay="true"
app:umanoPanelHeight="111dp"
app:umanoShadowHeight="0dp">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.gms.maps.MapView
android:id="#+id/map_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="96dp" />
<View
android:id="#+id/mapCover"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/transparent"/>
</FrameLayout>
<LinearLayout>
.
.
.
</LinearLayout>
</com.sothree.slidinguppanel.SlidingUpPanelLayout>
And in the code:
this.mMapCover = findViewById(R.id.mapCover);
this.mMapCover.setOnTouchListener(new View.OnTouchListener()
{
#Override
public boolean onTouch(View v, MotionEvent event)
{
if (mSlidingPanel.getPanelState() != PanelState.COLLAPSED)
{
mSlidingPanel.setPanelState(PanelState.COLLAPSED);
return true;
}
return false;
}
});
//to hide when you touch overlay
mLayout.setFadeOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
}
});
to hide it totaly
mLayout.setFadeOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mLayout.setPanelState(SlidingUpPanelLayout.PanelState.HIDDEN);
}
});
you can add a onClickListener or onTouchListener to the outside panel and ontouch or oncick call slidingPanelLayout.expandPane(0)
findViewById(R.id.bg_panel).setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
slidingPanelLayout.expandPane(0);
return false;
}
The answered code didn't work for me. Unfortunately this event does not capture a click on part of my UI.
So for doing that, set an id for main content (first child of SlidingUpPanelLayout) and call this:
findViewById(R.id.main_content).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(slidingUpPanelLayout.getPanelState() != SlidingUpPanelLayout.PanelState.COLLAPSED){
slidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
}
}
});
Your slidingUp panel contain whole screen then how you detect the click on outside of screen. If you are targeting it to only for "HideMenu" and "ShowMenu" then it is possible to do.
Otherwise with Full Screen SlidingUp Panel you can able to hide it by click back android button or any click event on the panel it self.
Let me know if my answer not resolve your problem.
Enjoy Coding... :)