"Ugly" CardView on Pre-Lollipop devices - android

I am using CardView as custom item for RecyclerView. They looks good on Android 5+ but so different on older Android versions.
On Android 5 +
On Android < 5
The code is the same:
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
card_view:cardCornerRadius="1dp"
card_view:cardElevation="1dp">
... other items ...
</android.support.v7.widget.CardView>
Is there a way to achieve the Android 5+ behavior on pre-Lollipop devices?

Using the support CardView? No.
Personally I think that the support CardView is broken and shouldn't be used at all. It looks and works a little bit different on Lollipop and on older systems. The shadow is different, the padding is different, content clipping doesn't work on pre-Lollipop devices, etc. The API is also weird and confusing. That's why it's hard to get good results on all platforms. If you can live without cards, I would go that way.
Of course it's possible to create a custom, nice-looking, backwards-compatible card, but it's a bit complex task. To create a card on your own you have to implement:
rounded corners with content clipping (doesn't work in the support CardView). Here's how to do it properly.
shadows drawn outside the card (not inside, like the support CardView). This one depends on your needs. I would override drawChild(...) in a parent container, where I could draw shadows around cards freely. Shadow generation method doesn't matter - it could be a gradient, a static 9-patch or a RenderScript-blurred black shape.
I was frustrated by the look and the API of CardView as well, so I created my own implementation. It can be found on GitHub - the library is called Carbon and using it is probably the easiest way to get a decent card. After importing the library simply add style="?attr/carbon_cardViewStyle" to any layout to make it look like a card:
<carbon.widget.RelativeLayout
style="?attr/carbon_cardViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>

Related

Is there an alternative to Android's ViewOverlay?

Android's ViewOverlay allows easy overlaying of Drawables over a conventional view element without having to interfere with the layout, but it is only available since API 18 (and as far as I can see, there's no compatibility layer, that backports this).
What would be the easiest alternative for an older API level (specifically: 15) to overlay a Drawable over a view element without modifying the layout? In my case, it would be enough to deal with a single Drawable; I don't need to replicate support for multiple Drawables that the new overlay API allows.
Did you try FrameLayout's foreground attribute?
FrameLayout's foreground is drawn over it's contents. And this works with API level 15.
In your xml:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foreground="#drawable/your_drawable">
...
Your contents.
...
</FrameLayout>
Or in your source code:
frameLayout.setForeground(drawable);

Using Material Design Components in Android Studio

I'm building my first app with Android studio and I'd like to use Material Design components such as cards in it.
I think I know how to add these by writing code, but I was wondering, is it possible to add cardview, recycleview etc. in design tab's palette somehow?
Whenever I used Android Studio to create material design apps, I always created the card through xml. It is then displayed correctly in the design view. However the widgets on the left hand side of the design view are only simple images, which are part of the actual Android API (do not need any extra references like in your case). To add a card to your page use the following xml:
<android.support.v7.widget.CardView
android:id="#+id/cardview"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:elevation="100dp"
card_view:cardBackgroundColor="#color/cardview_initial_background"
card_view:cardCornerRadius="8dp"
android:layout_marginLeft="#dimen/margin_large"
android:layout_marginRight="#dimen/margin_large"
>
//add any widgets which you want inserted in the card here
</android.support.v7.widget.CardView>
Code has been taken from here.
Hope this helps :)

What is the ButtonBarLayout and how should we use it?

When I developed, I found a new widget called android.support.v7.widget.ButtonBarLayout unexpectedly. I tried to search it on the internet, but nothing was found, even on the official development documents site.
In the meantime, I found two ButtonBarLayout when I search ButtonBarLayout everywhere in Android Studio, one is android.support.v7.widget.ButtonBarLayout and the other is com.android.internal.widget.ButtonBarLayout. I tried to read source codes of both, I found that they are the same except package name. So I thought maybe android.support.v7.widget.ButtonBarLayout came from com.android.internal.widget.ButtonBarLayout after the internal ButtonBarLayout was through tests and released. At the same time, ButtonBarLayout is inherited from LinearLayout.
But there are some question:
What can we get from ButtonBarLayout literally and how should we use it?
I noticed the variable of private boolean mAllowStacking. When it changes, orientation of this layout would be changed. But I didn't really understand what it is used for.
So does somebody know ButtonBarLayout well?
P.S.: I used Android Studio of 2.0.0 Preview 4 and Gradle Plugin of 2.0.0-alpha3 and Android Support Library of 23.1.1 and Platform-tools of 23.1 and Build-tools of 23.0.2.
As others pointed out, the class description tells exactly what it is: an extension of LinearLayout that automatically switches to vertical orientation when it can't fit its child views horizontally.
I might add that this was clearly done to fit with the material design specifications about dialogs. They make a distinction between side by side buttons and stacked buttons. See for example:
Side-by-side buttons are recommended when the text of each label does
not exceed the maximum button width, such as the commonly used
OK/Cancel buttons.
While you should go for stacked buttons when the single button is too large, or there's not enough room for both:
When text labels exceed the maximum button width, use stacked buttons
to accommodate the text. Affirmative actions are stacked above
dismissive actions.
So, one possible use of this class, is when designing your own dialogs. For example, AlertDialog and AlertDialog.Builder offer internal support for dialogs with buttons, but sometimes you just want to subclass DialogFragment or AppCompatDialogFragment for a better control.
There, it might be useful to setup a bottom button bar that follows the design guidelines, and have full control on the buttons (like enabling and disabling, things you can't do with an AlertDialog AFAIK).
The source code describes ButtonBarLayout as follows:
/**
* An extension of LinearLayout that automatically switches to vertical
* orientation when it can't fit its child views horizontally.
*/
So, in essence, it is nothing but a smart LinearLayout which manages auto-switching orientations based on available space on screen.
The same ButtonBarLayout.java file describes mAllowStacking in comments as follows:
/** Whether the current configuration allows stacking. */
Source Code Here
You are right first of all. ButtonBar layout does not seem to be featured anywhere in the official Android documentation. I tried myself to search about it, but to no avail. However I have found some information which defines what is a ButtonBar layout and when to use it. Hopefully this will help you.
Most tutorials use the Buttonbar layout in a dialogbox or at the bottom of a screen to confirm or decline an option. The image below is a visual representation of how the ButtonBar layout has been used in a screen.
The screenshot above has the following layout xml:
<LinearLayout
style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<Button
android:id="#+id/Button01"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Show" />
<Button
android:id="#+id/Button02"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Change" />
</LinearLayout>
<EditText
android:id="#+id/myView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10" >
<requestFocus />
</EditText>
So essentially what Android is doing here is simply creating two buttons next to each other in a LinearLayout with each button having the match_parent parameter set to the width. Hence each button takes half the size of the screen. Android have actually taken away the hassle of creating seperate buttons and positioning them correctly to fit different screens, by creating a simple widget handling this altogether.
As with the support library, Android have implemented this for developers using an earlier API. It is normal for them to use the support library for this purpose.
Hope this helps :)
https://android.googlesource.com/platform/frameworks/base/+/master/core/java/com/android/internal/widget/ButtonBarLayout.java
Looking into the code, I think it's a LinearLayout for buttons (duh). You can probably look at it like the Dialog buttons divided by a vertical spacer: | . AllowStacking will change the orientation to vertical and the gravity to the right instead of bottom. I should try it out to give a better answer
ButtonBarlayout is not featured anywhere in the official Android documentation.
it is used for auto-switching orientations according to the space.
Regarding your question:
How should we use it?
I guess it is undocumented because it is not stable yet.
It just popped up because this long lasting complaint originate from poor ROM modification by device vendor.
https://code.google.com/p/android/issues/detail?id=78377
See #270 for the resolution regarding classpath and why all classes inside .internal. were made public.
And nope even that fix a lot of bugs from poor ROM modification are still out there (in lots of device of well known brands). The issue is soon declined by project member.
I don't think we should use it just yet until the document show up.
Just my $.02 though.
Just to add to the other answers, if you guys want to check the orientation of a ButtonBarLayout you should check the orienation AFTER the value has called on measure.
In other words (Kotlin):
buttonBarLayout.post {
val orientation = buttonBarLayout.orientation
val height = buttonBarLayout.measuredHeight
}

GPU Artifacts on Android app

I've an app which have a very strange problem. Sometimes when launching the app all the fragments suffer this issue. It looks like the GPU is doing something fancy with it.
In the animated Gif below you can clearly see a PreferenceFragment which has the above mentioned artifacts.
Edit 1. it looks like i've a specific fragment that once "draws" on screen the bug appears. Switching fragment using .replace won't actually "clean" the problem.
How can i debug this?
it looks like garbage coming from the GPU.
The first thing you need to look at is your window background (and after that, the various fullscreen layouts backgrounds).
It is probably defined as null or another similar issue.
Ok my problem was caused because I had within my theme
<item name="android:windowBackground">#color/dark</color>
where dark is a color item set to #313131
Then i had a view that I used as a cover over some other piece of content to darken the underlying views as follow:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="200dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="#drawable/mydrawable"
/>
<View
android:id="#+id/coverview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/dark"/>
</FrameLayout>
Programmatically I had to set the alpha of the coverview by using coverView.getBackground().setAlpha((int)(0.6f*255));
Surprisingly this new alpha value was also applied to the window background and generated a lot of artifacts you see in my question (even after switching fragment where the coverview was no longer in the view tree)
By default, all drawables instances loaded from the same resource share a common state. Hence my code was wrong and i needed to call .mutate on the background before applying the new alpha value.
Except for me being wrong # calling .setAlpha without calling .mutate() the GPU flickering is still a mistery to me.
Why the GPU Flickers then? As pointed out by Christophe here:
the GPU flickers cause the background is now translucent and the
system tries to blend it with what was currently in the empty buffer
(mostly garbage).
To showcase the bug problem I created a github repo with a sample project showcasing the issue here: https://github.com/vekexasia/gpuflickering-background-alpha
Workarounds: Several. If you like to use the same approach (which also causes overdrawing) you should set the color programmatically and then set the alpha value to it. Like so:
coverView.setBackground(new ColorDrawable(getResources().getColor(R.color.dark)));
A better approach would be to apply a ColorFilter to the ImageView or if you're using Picasso use a Transformation
Another approach would be to just call .mutate() before .setAlpha()

How to display the image inside the android widget background?

I have a widget layout xml which sets the src to the delivered android widget 4x1 frame image.Here is the widget layout code.
<?xml version="1.0" encoding="utf-8" ?>
<AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/tuwidget" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView android:id="#+id/tuwidget_img_btn"
android:src="#drawable/widgetinitial"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</AbsoluteLayout>
#drawable/widgetinitial holds the widgetinitial.png image example 4x1 at developer.android.com (AppWidget design guidelines).(4x1_Widget_Frame_Portrait.psd) What I am trying to do is display an image inside the delivered frame instead what happens is the frame image goes away and only the image I am trying to display shows up. How can I display the image inside the bounding box or the background?
Any help is much appreciated.
Another question - I think I saw in a couple of forums AbsoluteLayout is a deprecated feature for Android 2.1 and above. Is that correct? and does using AbsoluteLayout throws any force close or other exceptions?
AbsoluteLayout is deprecated. It doesn't throw any exceptions, but its generally a bad idea to use it because it is really hard to design a layout that will work on all screen sizes. There is generally a better way to do it using a different layout.
In your case, I don't follow exactly, but it sounds like you want to layer two widgets on top of each other? An image with a frame? To do so, I'd use a FrameLayout. This is designed for having multiple layers of images.
Common layout objects is also a good guide to the basic layout types.
Take a look at the end of the page 2 of this pdf

Categories

Resources