The SwipeRefreshLayout was added to the Android Support Library in v19.1.0:
https://developer.android.com/reference/android/support/v4/widget/SwipeRefreshLayout.html
I have now added it to my application and it works as advertised. However, if I swipe slowly, I notice the overscroll is very jerky. See this video to see what I mean:
https://www.youtube.com/watch?v=HAuadMZYRf0
I see the issue using multiple layouts of varying levels of complexity. The layout shown in the video linked above is:
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/swipe_refresh_widget"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="#+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v4.widget.SwipeRefreshLayout>
And the relevant Java code:
#Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
final Bundle savedInstanceState) {
final View v = inflater.inflate(R.layout.list, container, false);
mSwipeRefreshWidget = (SwipeRefreshLayout) v.findViewById(R.id.swipe_refresh_widget);
mSwipeRefreshWidget.setOnRefreshListener(this);
mSwipeRefreshWidget.setColorScheme(R.color.refresh1, R.color.refresh2, R.color.refresh3,
R.color.refresh4);
// snip
}
Full source on Github:
https://github.com/meisteg/PickTheWinner/blob/d764c69af0c72a2d1173c6fb4328bd85088d8b59/app/src/com/meiste/greg/ptw/tab/Schedule.java
Anyone have suggestions how to fix the jerkiness? The overscroll should be smooth, no matter how long I take to overscroll the required distance. Thanks!
This is fixed in the 20.0 version of the support-library v4.
If you are using gradle :
compile 'com.android.support:support-v4:20.0'
Related
I have an activity LoginActivity and a fragment GoogleSignInFragment. The login logic for the app works perfectly okay but the only thing bugging me is the fact that the GoogleSignInFragment didn't show up in the XML preview for activity_login.xml. Without it showing up, it'll be very difficult for me to move my code to Fragments, since I can't style the activity as well anymore
Here's all the relevant code...
LoginActivity.java
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.sign_in_google, new GoogleSignInFragment())
.commit();
}
activity_login.xml
<FrameLayout
android:id="#+id/sign_in_google"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:layout="#layout/fragment_sign_in_google_button"/>
GoogleSignInFragment.java
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_google_sign_in_button, container, false);
return v;
}
fragment_google_sign_in_button.xml
<?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:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
tools:context=".GoogleSignInFragment">
<com.google.android.gms.common.SignInButton
android:id="#+id/sign_in_google_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" />
</FrameLayout>
Sometimes Android Studio fails to load preview so you can try Invalidate Cache / Restart option in File option.
For this, Go to File > Click on Invalidate Cache / Restart
https://developer.android.com/studio/write/tool-attributes#toolslayout
Says it's meant for intended for: <fragment> but you are using a FrameLayout
I think they're no problem with your code ad your fragment activity_login.xml does not contain anything and fragment_google_sign_in_button.xml contains com.google.android.gms.common.SignInButton is used with help of an external library so sometimes preview not shows.
I am using the androidX CardView.
In order to make a recyclerview adapter for the cards I need to inflate the custom card layout I have made.
Unfortunately the xml of the custom card view doesn't appear in R.layout.(xml_name),while i find normally all the other layouts.
This the code inside the adapter class where I need to inflate:
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view;
LayoutInflater mInflater = LayoutInflater.from(mContext);
view = mInflater.inflate(R.layout.cardview_item_answer); // not finding this layout
return null;
}
This is the card layout xml (cardview_item_answer.xml) :
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="160dp"
android:layout_height="160dp"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_margin="5dp"
app:cardCornerRadius="4dp"
>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/image_card"
android:layout_width="match_parent"
android:layout_height="100dp"
android:scaleType="centerCrop"
android:background="#2d2d2d2d">
</androidx.appcompat.widget.AppCompatImageView>
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/text_card"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="test"
android:textSize="13sp"
android:textColor="#color/black"
android:layout_gravity="center"
></androidx.appcompat.widget.AppCompatTextView>
</androidx.appcompat.widget.LinearLayoutCompat>
Do you suggest any ideas ?
Thank you very much !
Solved !
If you ever have this problem -> clean project and invalidate Caches/Restart.
If the XML contains any errors, your build will likely fail - this will cause R.layout to yield unexpected results. I am not seeing a closing tag for the CardView in cardview_item_answer.xml
Try adding a closing tag </androidx.cardview.widget.CardView> at the end of cardview_item_answer.xml
For some reasons neither of the solutions above worked for me.
I just had to use the full path to the layout before being able to use it. In my case the item of a spinner
So I did this:
import com.myProject.R.layout.spinner_item
It is never a suggested autocompletion, at least in my case, in model views. It is in the fragments. Can't make sense of that, but it works in view models too.
I use fragments with ViewPager2, and I notice two relevant IllegalStateExceptions in production (I can't reproduce it myself) occurring in devices like Xiaomi, Yulong, asus, vivo running Android 8 or 9:
Fatal Exception: java.lang.IllegalStateException: Page can only be offset by a positive amount, not by -758
at androidx.viewpager2.widget.ScrollEventAdapter.updateScrollEventValues(ScrollEventAdapter.java:280)
at androidx.viewpager2.widget.ScrollEventAdapter.onScrolled(ScrollEventAdapter.java:178)
at androidx.recyclerview.widget.RecyclerView.dispatchOnScrolled(RecyclerView.java:5173)
at androidx.recyclerview.widget.RecyclerView$ViewFlinger.run(RecyclerView.java:5338)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1029)
at android.view.Choreographer.doCallbacks(Choreographer.java:834)
at android.view.Choreographer.doFrame(Choreographer.java:760)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1015)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:224)
at android.app.ActivityThread.main(ActivityThread.java:7083)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:928)
and
Fatal Exception: java.lang.IllegalStateException: Page(s) contain a ViewGroup with a LayoutTransition (or animateLayoutChanges="true"), which interferes with the scrolling animation. Make sure to call getLayoutTransition().setAnimateParentHierarchy(false) on all ViewGroups with a LayoutTransition before an animation is started.
at androidx.viewpager2.widget.ScrollEventAdapter.updateScrollEventValues(ScrollEventAdapter.java:272)
at androidx.viewpager2.widget.ScrollEventAdapter.onScrolled(ScrollEventAdapter.java:178)
at androidx.recyclerview.widget.RecyclerView.dispatchOnScrolled(RecyclerView.java:5173)
at androidx.recyclerview.widget.RecyclerView$ViewFlinger.run(RecyclerView.java:5338)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1029)
at android.view.Choreographer.doCallbacks(Choreographer.java:841)
at android.view.Choreographer.doFrame(Choreographer.java:769)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1015)
at android.os.Handler.handleCallback(Handler.java:794)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:176)
at android.app.ActivityThread.main(ActivityThread.java:6651)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:824)
While looking around, I passed by the following thread: https://issuetracker.google.com/issues/129530305, which as I understood correctly, seems to summarize to set animateLayoutChanges to false on any parent layout of viewPager2 and that's what I did. Unfortunately, that didn't solve my issue. Then I found another thread: java.lang.IllegalStateException: Page can only be offset by a positive amount, which seems to not help a lot. Any idea what else might cause the issue?
You need to perform this on each of your pages/Fragments
View view = layoutInflater.inflate(R.layout.page, parent, false);
ViewGroup viewGroup = view.findViewById(R.id.animated_viewgroup);
viewGroup.getLayoutTransition().setAnimateParentHierarchy(false);
As looking at official documentation here
https://developer.android.com/reference/androidx/viewpager2/widget/ViewPager2
It states that
If your pages contain LayoutTransitions, then those LayoutTransitions
must have animateParentHierarchy set to false. Note that if you have a
ViewGroup with animateLayoutChanges="true" in your layout xml file, a
LayoutTransition is added automatically to that ViewGroup. You will
need to manually call
getLayoutTransition().setAnimateParentHierarchy(false) on that
ViewGroup after you inflated the xml layout.
same your error log talks about on very first line
Fatal Exception: java.lang.IllegalStateException: Page(s) contain a ViewGroup with a LayoutTransition (or animateLayoutChanges="true"), which interferes with the scrolling animation. Make sure to call getLayoutTransition().setAnimateParentHierarchy(false) on all ViewGroups with a LayoutTransition before an animation is started.
I had the second error when used viewPager2.
As commented above, in my fragment layout I found viewGroup with animateLayoutChanges="true", then in fragment OnViewCreated I called
viewGroup.getLayoutTransition().setAnimateParentHierarchy(false);
That's fix my problem!
Another Problem I had was, that I did not got the above exception, but my viewpager2 started to flick / reload the page, when the transition started. I was a bit confused by these examples and how to add set setAnimateParentHiearchy to false, so I would like to add another example, how to fix this problem:
ViewPager Layout
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Toolbar -->
<include
android:id="#+id/headline"
layout="#layout/registration_toolbar"
app:isBold="#{false}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:outsideToolbarTitle="#{#string/fragment_user_data_toolbar_title}" />
<com.google.android.material.tabs.TabLayout
android:id="#+id/tl_user_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/headline" />
<!-- This Viewpager contains a fragment, which has android:animateLayoutChanges="true" -->
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/vp_user_data"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/tl_user_data" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Fragment Layout (which will be inside the viewpager)
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/outerContraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true" > <-- THIS WILL CAUSE PROBLEMS
<!-- Deleted unnecessary parts -->
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
If you want to keep android:animateLayoutChanges, you have to call this inside your fragments onViewCreated()
class MyFragmentInsideViewPagerThatHasProblems : Fragment(R.layout.above_layout) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val viewGroup = requireView().findViewById<ConstraintLayout>(R.id.outerContraintLayout) as ViewGroup
viewGroup.layoutTransition.setAnimateParentHierarchy(false)
}
}
I have the following layout (cut down to a minimal example)
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<VideoView
android:id="#+id/videoView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<EditText
android:id="#+id/editText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#color/free_text_default_bg"
android:gravity="center_horizontal"
android:padding="#dimen/free_text_padding"
android:text="background element is a video view" />
</RelativeLayout>
The problem is, that if I move the EditText around (it has a drag listener attached to it), it dissapears at a certain point of the screen as you can see in this screenshot.
The problem seems to only appear when I have a VideoView. Even if I don't show a video.
When I replace it with e.g. an ImageView there is no problem at all.
So my question is, what's happening and how can I resolve this?
Thanks for your time!
UPDATE 1: This only happens if the VideoView has
android:layout_height="match_parent"
when I set it to some "dp" value everything works just fine...
I did not find a solution to my question jet but I have found a workaround:
Once the layout is finished, I get the height from the parent container, subtract 1px and set this as new height for the video view.
The 1px is barely visible...
Here is the code:
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
ViewGroup.LayoutParams layoutParams = videoView.getLayoutParams();
layoutParams.height = parentView.getHeight() - 1;
videoView.setLayoutParams(layoutParams);
}
at the moment I'm calling and building fragments like this:
if (getSupportFragmentManager().findFragmentById(R.id.fragment_list) == null) {
list = new MyListFragment();
getSupportFragmentManager().beginTransaction().add(R.id.fragment_list, list).commit();
}
But I wonder if this is so called best practice, because this seems to me to be much boilerplate code. Are there better ways?
Use XML layouts and Fragment classes. Here I create a layout with 2 fragments. The class inflates the layout fragment_actionbarcompat.xml (that code isn't shown here but it's a basic layout file). And I create a layout file for the activity that houses the 2 fragments.
The ActionBarCompatFragment class overrides the onCreateView method to inflate it's layout. That gets injected into the fragment tag layoutwise.
In your case normally you don't just add in a plain ListFragment, you extend ListFragment and add your custom code into it. It's a way of better supporting fancy patterns like Model-View-Controller. Fragments are meant to be isolated compartments so you can reuse them between activities if you'd like. In most cases your class will hold the logic to load the data that the fragment needs.
ActionBarCompatFragment.java
#Override
public final View onCreateView(LayoutInflater inflater, ViewGroup root, Bundle savedInstanceState) {
final int layoutId = R.layout.fragment_actionbarcompat;
return inflater.inflate(layoutId, root, false);
}
File: activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<fragment
android:id="#+id/ActionBarCompatFragment"
android:layout_width="#dimen/ActionBarSize"
android:layout_height="match_parent"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
class="com.packagename.app.ActionBarCompatFragment" >
<!-- Preview: layout=#layout/fragment_actionbarcompat -->
</fragment>
<fragment
android:id="#+id/ComposerFragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_toRightOf="#id/ActionBarCompatFragment"
class="com.packagename.app.ComposerFragment" >
<!-- Preview: layout=#layout/fragment_composer -->
</fragment>
</RelativeLayout>