I'm trying to create a custom "loading/throbber" icon for my app. I have an ImageView that points to my "loading" icon:
The problem is the rotation is off axis and looks "wobbly", but I'm not sure what I'm doing wrong:
<ImageView
android:id="#+id/headerReload"
android:src="#drawable/reload"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingRight="5dip"
android:onClick="headerReload_onClick"
android:layout_alignParentRight="true"
/>
public void headerReload_onClick(final View v) {
ImageView searchSpinner = (ImageView)findViewById(R.id.headerReload);
Animation spinnerAnimation = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.loading);
searchSpinner.startAnimation(spinnerAnimation);
}
loading.xml
<?xml version="1.0" encoding="UTF-8"?>
<rotate
xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
android:interpolator="#android:anim/linear_interpolator"
android:duration="1200"
/>
5dp padding on the right side will cause it to rotate unevenly
Related
I have two activities A and B and Three fragments C,D and E.
I used intent to move from activity A to B and didn't finish activity A as I want to get back to it later.
Activity B contains a custom layout and a fragment container.
Custom layout contains a text view and a back icon image view. This layout act as action bar as I do not use default action bar as theme of activity B is android:theme="#style/AppTheme.NoActionBar".
Fragment Container hosts a navigation graph.
Fragment C is Start destination and navigation graph allows navigation as C->D->E
and back stack in following order.
C->D //After first back Stack
C //After Second back Stack reached final element in back stack.
A //Activity B is finish and Activity A is resumed after pressing one more back icon image
On press back icon image view of custom layout in activity B xml.
As there is no other action bar in any Fragment(C, D, E) so I can only use back icon image view of activity B.
I want to know how I can achieve this functionality
My activity B xml is as follows
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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">
<!--android:id="#+id/container"-->
<include layout="#layout/custom_tool_bar"
android:id="#+id/toolbar_settings_activity"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<androidx.fragment.app.FragmentContainerView
android:id="#+id/nav_host_dashboard_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="#navigation/settings_navigation"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#id/toolbar_settings_activity"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
xml code for custom tool bar is given bellow
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="wrap_content"
android:background="#drawable/app_gradient_color_background">
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/iv_back"
android:layout_width="20dp"
android:layout_height="26dp"
android:layout_marginStart="10dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:layout_marginEnd="1dp"
android:contentDescription="#string/content_description"
android:scaleType="fitXY"
android:src="#drawable/ic_white_color_back_24dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
/>
<LinearLayout
android:id="#+id/customlayout_width"
android:layout_width="wrap_content"
android:layout_height="?attr/actionBarSize"
app:layout_constraintStart_toEndOf="#+id/iv_back"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
<TextView
android:id="#+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingEnd="10dp"
android:paddingTop="6dp"
android:paddingStart="10dp"
android:text="#string/custom_tool_bar_Name"
android:textColor="#color/colorOffWhite"
android:textSize="26sp"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
custom layout used as action bar
Activity B when navgraph start destination is set to C fragment
Navgraph to navigate from fragment C->D->E in Activity B
1- First in your Activity B get the navController:
val navHostFragment = binding.navHostDashboardFragment.getFragment<NavHostFragment>()
val navController = navHostFragment.navController
2- Then you can add a click listener to your back button:
binding.toolbarSettingsActivity.ivBack.setOnClickListener {
val isStackPopped = navController.popBackStack() // Navigate back to the last fragment
if (!isStackPopped) finish() // If there is no fragments in the stack finish the activity and go back to activity A
}
I decided to use Lottie animation to switch from activity to activity.
My animation covers the entire screen, and I would like to have half the animation time played on the first activity and the rest on the second activity
I tried using Shared Element Transition but it doesn't work properly because when new activity starts first what i see is new activity and then after few miliseconds Lottie animation resumes. I would like to avoid showing new activity before Lottie animation is over
First Activity XML file
<RelativeLayout android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.airbnb.lottie.LottieAnimationView android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/newActivityAnimation"
app:lottie_fileName="transitionAnimation.json"
android:scaleType="centerCrop"
android:elevation="5dp"
android:transitionName="sharedNewActivityTransition"
/>
Second Activity XML file
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.MenuActivity">
<com.airbnb.lottie.LottieAnimationView android:layout_width="match_parent"
android:layout_height="match_parent"
app:lottie_fileName="transitionAnimation.json"
android:scaleType="centerCrop"
android:id="#+id/changeActivityAnimationMenu"
android:elevation="5dp"
android:transitionName="sharedNewActivityTransition"
/>
</RelativeLayout>
Here is function which start changing activite
override fun onLanguageClickListener(position: Int) {
animateCollapseMenuSlide(isMenuCollapsed)
newActivityAnimation.setMaxFrame(19)
delay.delayedFunction({newActivityAnimation.playAnimation()},200)
delay.delayedFunction({changeActivity()},1000)
}
.
.
.
private fun changeActivity() {
val intent = Intent(this,MenuActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NO_ANIMATION
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(this,activityTransitionAnimation,
"sharedNewActivityTransition")
startActivity(intent,options.toBundle())
}
The starting point here is the need to periodically rotate a vector drawing, like for instance the hands of an analog clock.
I understand that a vectorDrawable resource does not directly support rotation attributes. I can put the drawable inside a group which can rotate the drawable, but that rotation won't be modifiable at run time.
I came across many posts explaining rotating views or canvases, as well as the rotate drawable. The latter seems to be appropriate for my purpose.
I created a new activity with the help of Android Studio 3.5 to achieve a simple rotation with the help of a rotate drawable.
java/com.example.myapplication/MainActivity:
package com.example.myapplication
import android.graphics.drawable.RotateDrawable
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.core.content.ContextCompat
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val activity = this#MainActivity
val context = activity.applicationContext
val drawable = ContextCompat.getDrawable(context, R.drawable.rotation)
val rotation = drawable as RotateDrawable
rotation.setLevel(180)
}
}
res/layout/activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="match_parent"
tools:context=".MainActivity">
<ImageView
android:id="#+id/imageView2"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/rotation" />
</androidx.constraintlayout.widget.ConstraintLayout>
res/drawable/rotation.xml:
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:pivotX="50%" android:pivotY="50%"
android:fromDegrees="0" android:toDegrees="360"
android:drawable="#drawable/ic_launcher_foreground"/>
I expect that the setLevel function can be used to rotate the drawable, but it has no effect.
Changing the fromDegrees attribute in the rotation.xml does the rotation as expected. I am not sure if the maximum level for <rotate> should be 360 or 10000, but that is not the issue.
What am I missing? Is it a detail, or is the rotateDrawable the wrong approach altogether?
Related questions:
Rotating a drawable in Android
Programmatically rotate drawable or view
Is it possible to rotate a drawable in the xml description?
Try using imageView2.setRotation(180) instead of rotation drawable.
I need to change rotation from 30 to -30 degrees while motion scene repeating. How to do this programatically?
R.xml.scene_01:
<MotionScene
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:motion="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Transition
motion:constraintSetStart="#id/start"
motion:constraintSetEnd="#id/end"
motion:duration="2000">
<KeyFrameSet>
<KeyAttribute
android:rotation="30"
motion:target="#id/button"
android:scaleX="1.3"
android:scaleY="1.3"
motion:framePosition="50"/>
</KeyFrameSet>
</Transition>
<ConstraintSet android:id="#+id/start">
<Constraint
android:id="#id/button"
android:layout_width="64dp"
android:layout_height="64dp"
android:src="#drawable/tut_ali_icon"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</ConstraintSet>
<ConstraintSet android:id="#+id/end">
<Constraint
android:id="#id/button"
android:layout_width="64dp"
android:layout_height="64dp"
android:src="#drawable/tut_ali_icon"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</ConstraintSet>
</MotionScene>
Listener in MotionLayout:
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mMotionLayout.setTransitionListener(new MotionLayout.TransitionListener() {
#Override
public void onTransitionChange(MotionLayout motionLayout, int i, int i1, float v) {
}
#Override
public void onTransitionCompleted(MotionLayout motionLayout, int currentId) {
if(currentId == R.id.end){
motionLayout.loadLayoutDescription(R.xml.scene_02);
motionLayout.transitionToStart();
}else{
motionLayout.loadLayoutDescription(R.xml.scene_01);
motionLayout.transitionToEnd();
}
}
});
mMotionLayout.transitionToEnd();
}
I need to change rotation in onTransitionCompleted method. As an option maybe can I create KeyFrameSet which using various KeyAttribute?
In R.xml.scene_02 I change only android:rotation="-30" but it seems loadLayoutDescription method doesn't work.
Such a movement as in the picture I want to get:
enter image description here
Not enough rep to comment, but why do you have 2 separate transitions specified?
Trying to understand the question, if you want it to consistently rotate with that listener, the following should work:
<Transition
motion:constraintSetEnd="#id/end"
motion:constraintSetStart="#id/start"
motion:duration="2000">
<KeyFrameSet>
<KeyAttribute
android:rotation="-45"
android:scaleX="2"
android:scaleY="2"
motion:framePosition="0"
motion:target="#id/button" />
<KeyAttribute
android:rotation="45"
android:scaleX="2"
android:scaleY="2"
motion:framePosition="50"
motion:target="#id/button" />
<KeyAttribute
android:rotation="-45"
android:scaleX="2"
android:scaleY="2"
motion:framePosition="100"
motion:target="#id/button" />
</KeyFrameSet>
</Transition>
This will have the button start and end at -45 degrees rotation and, halfway through the transition, will rotate to 45 degrees. Note that these are just value animations so they will NOT be applied to the object when it reaches the start or end - you'll need to specify the rotation in the start/end ConstraintSet attributes.
I have an application which I need adapt for Android TV. This application contains horizontal RecyclerView and it doesn't scroll when I press D-pad buttons on remote control.
I found this solution, but it crashes.
Here is the code:
<ru.myapp.package.HorizontalPersistentFocusWrapper
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="250dp"
android:background="#null"
android:scrollbars="none"/>
</ru.myapp.package.HorizontalPersistentFocusWrapper>
HorizontalPersistentFocusWrapper is the same as PersistentFocusWrapper but mPersistFocusVertical = false;
Crash occure in this place:
#Override
public void requestChildFocus(View child, View focused) {
super.requestChildFocus(child, focused);
View view = focused;
while (view != null && view.getParent() != child) {
view = (View) view.getParent(); <<<------ Crash here
}
mSelectedPosition = view == null ? -1 : ((ViewGroup) child).indexOfChild(view);
if (DEBUG) Log.v(TAG, "requestChildFocus focused " + focused + " mSelectedPosition " + mSelectedPosition);
}
Crash stacktrace:
java.lang.ClassCastException: android.view.ViewRootImpl cannot be cast to android.view.View
at ru.myapp.package.HorizontalPersistentFocusWrapper.requestChildFocus(HorizontalPersistentFocusWrapper.java:108)
at android.view.View.handleFocusGainInternal(View.java:5465)
at android.view.ViewGroup.handleFocusGainInternal(ViewGroup.java:714)
at android.view.View.requestFocusNoSearch(View.java:8470)
at android.view.View.requestFocus(View.java:8449)
at android.view.ViewGroup.requestFocus(ViewGroup.java:2747)
at android.view.View.requestFocus(View.java:8416)
at android.support.v4.widget.NestedScrollView.arrowScroll(NestedScrollView.java:1222)
at android.support.v4.widget.NestedScrollView.executeKeyEvent(NestedScrollView.java:551)
at android.support.v4.widget.NestedScrollView.dispatchKeyEvent(NestedScrollView.java:512)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1640)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1640)
Use the lastest version of RecyclerView.
Or use at least
com.android.support:recyclerview-v7:23.2.0
See this link for more info:
https://code.google.com/p/android/issues/detail?id=190526&thanks=190526&ts=1445108573
Now for the important part:
New versions of RecyclerView started to obey the rules of its children (like height and width).
You must set your root view in child item XML to:
android:focusable="true"
Now, scrolling will go like it was intended.
Set the focusable to true in the root view of the recyclerview item. android:focusable="true" and apply a selector background to the root view item. Everything can be done in the xml files. With the following settings, the dpad or remote controller will be able to scroll up and down the list, the current selected item in the list will be highlighted.
The layout file for the root view of the list item in the RecyclerView: list_item.xml , the important parts here are android:background="#drawable/item_selector" and android:focusable="true"
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:background="#drawable/item_selector"
android:focusable="true">
<TextView
android:id="#+id/topic"
android:layout_width="match_parent"
android:layout_height="60dp"
android:textColor="#ffffff"
android:gravity="center"
tools:text="Education"/>
</LinearLayout>
The drawable selector file in the drawable folder: item_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#000000" android:state_pressed="true"/>
<item android:drawable="#000000" android:state_selected="true"/>
<item android:drawable="#000000" android:state_focused="true"/>
<item android:drawable="#03A9F4"></item>
</selector>
The layout file containing the RecyclerView: activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".MainActivity">
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</android.support.constraint.ConstraintLayout>
Try this. Works for me.
#Override
public void requestChildFocus(View child, View focused) {
super.requestChildFocus(child, focused);
View view = focused;
while (view != null && view.getParent() != child) {
try {
view = (View) view.getParent();
} catch (ClassCastException e) {
view = null;
}
}
mSelectedPosition = view == null ? -1 : ((ViewGroup) child).indexOfChild(view);
if (DEBUG)
Log.v(TAG, "requestChildFocus focused " + focused + " mSelectedPosition " + mSelectedPosition);
}