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.
Related
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
}
}
}
When a user will scroll Recyclerview from right to left at the same time the first view will disappear with fade-out animation according to scrolling and background view also be parallax like google play store app.
It will animated recylerview, "not the normal horizontal recyclerview". You can see this in google play, not every time, it appears occasionally
According to what you have written, you basically wanted a horizontal recycler view which when scrolled has a fade animation to the header.
I had encountered the same problem and I solved it the following was. I used kotlin, during my development.
Firstly you need to add a scroll listener to your recycler view, i have made an extension function for scroll
inline fun RecyclerView.scroll(
crossinline onScrolled: (RecyclerView, Int, Int) -> Unit = { it, dx, dy -> },
crossinline onScrolledChanged: (RecyclerView, Int) -> Unit = { it, newState -> }
) {
addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
onScrolled(recyclerView, dx, dy)
}
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
onScrolledChanged(recyclerView, newState)
}
})
}
Now create a divider item decorator for adding the padding to the first item
class DividerDecorator(val paddingStart: Float) : RecyclerView.ItemDecoration() {
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
super.getItemOffsets(outRect, view, parent, state)
val position = parent.getChildAdapterPosition(view)
if (position == 0 || position == 1) {
outRect.left = paddingStart.toInt()
}
val lp = view.layoutParams as StaggeredGridLayoutManager.LayoutParams
val spanIndex = lp.spanIndex
if (position >= 0) {
if(spanIndex==0) {
outRect.top = 13.px
outRect.bottom = 5.px
}else{
outRect.top = 5.px
outRect.bottom = 13.px
}
}
}
}
Now in your XML place add the image that you want as background and place the recycler view on top of that image.
Once you have done that you just need to add scroll extension to your recyclerview
private var overallScroll = 0;
recycler.scroll(onScrolled = { _, dx, _ ->
overallScroll += dx
backgroundView.animate()
.alpha(overallScroll.remap(padding))
.setInterpolator(LinearInterpolator())
.setDuration(0)
.start()
})
remap function i have maded to calculate the proper alpha corresponding to scroll displacement.
fun Int.remap(offset:Float):Float{
return 1-(this/offset)
}
and yes don't forget to make the recycler view horizontal.
You can use RecyclerView with following layout manager
LinearLayoutManager layoutManager = new LinearLayoutManager(MainActivity.this, LinearLayoutManager.HORIZONTAL, false);
recyclerView.setLayoutManager(layoutManager);
I got a problem when I wanted to put my large-sized photos from my storage into small imageViews. I´m wondering how to put these kind of photos inside a small imageViews without decreasing app speed or crashing.
I have a RecyclerView that shows some pictures from storage in a list. Here is my recycler adapter code.
Thanks for your help.
class Adapter(files: ArrayList<File>):RecyclerView.Adapter<Adapter.myViewHolder>() {
val files: ArrayList<File> = files
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): myViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_complex_note_view , parent , false)
return myViewHolder(view)
}
override fun getItemCount(): Int {
return files.size
}
override fun onBindViewHolder(holder: myViewHolder, position: Int) {
holder.bind(files.get(position))
}
class myViewHolder(itemView: View):RecyclerView.ViewHolder(itemView){
lateinit var imageView: ImageView
init {
imageView = itemView.findViewById(R.id.complex_note_view_item_imageview)
}
fun bind(file: File) {
imageView.setImageURI(file.toUri())
}
}
}
The better way is using this code:
Glide.with(imageView.context)
.load(imageFile)
.apply(RequestOptions().centerCrop())
.into(imageView)
Glide class is faster and more optimized.
i suggest you using picasso with resize methode ,it will avoid you the lack of speed and crashes
Picasso.get().load(new File(...)).resize(50, 50).centerCrop().into(imageView);
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/
I have a LinearLayout, and this LinearLayout will hold dynamically placed views. I need to find out what the width of the children of LinearLayout, however this has to be done in onCreate method. From researching I've found out that you can't use getWidth from this method. So instead I'm using onWindowFocusChanged, which works for the parent LinearLayout (returning me the actual size), but it doesn't work with its children.
Another thing I noticed is that when the screen is fading away and the screen is locked, I can see at the logs the actual width of the children being returned (I think the activity is being paused).
I'm really stuck and this is needed because I need to dynamically place those views depending on the children width.
You might be able to get with the below. But as others pointed out, this probably isn't a great idea.
LinearLayout.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
LinearLayout.getMeasuredWidth();
inside the onCreate , views still can't know the state of the nearby views and the children ,etc... so only after all is prepared and the layout process is done , you can get the size of views .
here's a quick code for getting the size of the view just before it's being drawn:
private static void runJustBeforeBeingDrawn(final View view, final Runnable runnable)
{
final ViewTreeObserver vto = view.getViewTreeObserver();
final OnPreDrawListener preDrawListener = new OnPreDrawListener()
{
#Override
public boolean onPreDraw()
{
Log.d(App.APPLICATION_TAG, CLASS_TAG + "onpredraw");
runnable.run();
final ViewTreeObserver vto = view.getViewTreeObserver();
vto.removeOnPreDrawListener(this);
return true;
}
};
vto.addOnPreDrawListener(preDrawListener);
}
alternatively , you can use addOnGlobalLayoutListener instead of addOnPreDrawListener if you wish.
example of usage :
runJustBeforeBeingDrawn(view,new Runnable()
{
#Override
public void run()
{
int width=view.getWidth();
int height=view.getHeight();
}
});
another approach is to use onWindowFocusChanged (and check that hasFocus==true) , but that's not always the best way ( only use for simple views-creation, not for dynamic creations)
EDIT: Alternative to runJustBeforeBeingDrawn: https://stackoverflow.com/a/28136027/878126
https://stackoverflow.com/a/3594216/1397218
So you should somehow change your logic.