So I have a Recycler View with Grid layout Manager (Image at the End) . Each Item has an ImageView, the code that works fine is below:
Picasso.with(mContext).load(item1.gadget_image).into(vh1.imgView, new Callback() {
#Override
public void onError() {
}
#Override
public void onSuccess() {
vh1.pgLoading.setVisibility(View.GONE);
}
});
Where vh1 is the view holder of the RecyclerView.Adapter that holds the widgets. But I needed a gradient above the ImageView (see Image below). The solution came with another Image View that had as src a drawable that was the Gradient . To implement this I had to set translation of the second ImageView (gradient) to 1dp more than the first ImageView, so as to have the gradient above the main Image. This is the code :
<ImageView
android:id="#+id/gradient"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/gradient"
android:layout_alignTop="#+id/imgItem"
android:layout_alignParentLeft="true"
android:contentDescription="#string/app_name"
android:layout_alignParentStart="true"
android:translationZ="1dp" />
<ImageView
android:id="#+id/imgItem"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:contentDescription="#string/app_name"
android:scaleType="centerCrop"/>
gradient :
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<gradient
android:angle="90"
android:endColor="#15ffffff"
android:startColor="#e1181818"
android:centerColor="#4e232323" />
<corners android:radius="0dp" />
</shape>
But this solution works only for APIs >= Lollipop, for APIs below Lollipop doesn't work. So I thought that it is better to have one ImageView and Picasso should load the images into ImageViewes as a background and having the gradient as src. Code below:
Picasso.with(mContext).load(item.gadget_image).into(new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom loadedFrom) {
vh.pgLoading.setVisibility(View.GONE);
//setting background
vh.imgView.setBackground(new BitmapDrawable(mContext.getResources(), bitmap));
}
#Override
public void onBitmapFailed(Drawable drawable) {
}
#Override
public void onPrepareLoad(Drawable drawable) {
}
});
and in the XML file :
<ImageView
android:id="#+id/imgItem"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:contentDescription="#string/app_name"
android:scaleType="centerCrop"
android:src="#drawable/gradient"/>
In the last implementation the problem is that it is too slow to load the images and it confuses the Images with the content. For example if someone clicks to the third ImageView might displays content from the second Item.
My question is how can I implement a solution to have Gradient above the Images for All the APIs without using traslation. like this :
Thanks for the help
Related
I am looking for a custom widget to draw a circle with multiple border colors.
Say for example if my total circle represent 0-360, I need to color my circle border with different colors.
For example, I need to mark 0-60 with red, 61-120 with green, 121-300 with magenta and 301-360 with yellow border color.
please suggest me how I can do it in android.
You application is pretty simple. I don't recommend your using an external library. You can quickly implement a class that draws and manages your desired shape. An example is presented:
public class DifferentColorCircularBorder{
private RelativeLayout parentLayout;
public DifferentColorCircularBorder(RelativeLayout parentLayout) {
this.parentLayout = parentLayout;
}
public void addBorderPortion(Context context, int color, int startDegree, int endDegree) {
ProgressBar portion = getBorderPortion(context, color, startDegree, endDegree);
parentLayout.addView(portion);
}
private ProgressBar getBorderPortion(Context context, int color, int startDegree, int endDegree) {
LayoutInflater inflater = LayoutInflater.from(context);
ProgressBar portion = (ProgressBar) inflater.inflate(R.layout.border_portion, parentLayout, false);
portion.setRotation(startDegree);
portion.setProgress(endDegree - startDegree);
portion.getProgressDrawable().setColorFilter(color, Mode.SRC_ATOP);
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) portion.getLayoutParams();
params.addRule(RelativeLayout.CENTER_IN_PARENT);
portion.setLayoutParams(params);
return portion;
}
}
border_portion is defined as below:
<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="220dp"
android:layout_height="220dp"
android:progressDrawable="#drawable/circle_exterior"
android:layout_centerInParent="true"
android:max="360"/>
circle_exterior is defined here:
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="ring"
android:innerRadius="100dp"
android:thickness="10dp" >
<solid android:color="#ff111111" />
</shape>
The MainActivity class is defined like this:
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RelativeLayout interiorLayout = (RelativeLayout) findViewById(R.id.interior);
DifferentColorCircularBorder border = new DifferentColorCircularBorder(interiorLayout);
border.addBorderPortion(getApplicationContext(), Color.RED, 0, 40);
border.addBorderPortion(getApplicationContext(), Color.GREEN, 40, 90);
border.addBorderPortion(getApplicationContext(), Color.BLUE, 90, 270);
border.addBorderPortion(getApplicationContext(), 0xFF123456, 270, 360);
}
}
finally activity_main layout is:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin" >
<RelativeLayout
android:id="#+id/interior"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<View
android:layout_width="200dp"
android:layout_height="200dp"
android:background="#drawable/circle_interior_bg"
android:layout_centerInParent="true" />
</RelativeLayout>
</RelativeLayout>
Explanation about the dimensions: This is an example. Here, I have picked the dimensions to fit the circle perfectly. Change these based on your application.
Image sample:
i just created a simple Library for that purpose CircularStatusView , it was inspired by WhatsApp Status and it's easy to use.
first up add the view, in my case i've added it around CircleImageView but you can use on any view.
<RelativeLayout
android:id="#+id/image_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent">
<de.hdodenhof.circleimageview.CircleImageView
android:layout_width="75dp"
android:layout_height="75dp"
android:layout_centerInParent="true"
android:padding="6dp"
android:src="#mipmap/ic_launcher" />
<com.devlomi.circularstatusview.CircularStatusView
android:id="#+id/circular_status_view"
android:layout_width="75dp"
android:layout_height="75dp"
android:layout_centerInParent="true"
app:portion_color="#color/colorAccent"
app:portion_spacing="4dp"
app:portion_width="4dp"
app:portions_count="8" />
</RelativeLayout>
you can set the portions count Programmatically by using:
circularStatusView.setPortionsCount(count);
and for the portions color:
circularStatusView.setPortionsColor(color);
you can also set specific color for every portion:
circularStatusView.setPortionColorForIndex(/*index of portions starting from first portion at the top CW */ i, color);
for this you can try this library that i had come across
https://github.com/mucahitsidimi/GaugeView might be useful.
uses a custom view of fixed lengths to render the circle by using canvas
I am learning about animation in Android and have following code (based on Android-Developer tutorial) which works when I click on the ImageButton. On click, it will zoom-in and then on next click, it will zoom-out.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.container_layout);
final View thumb1View = findViewById(R.id.thumb_button_1);
thumb1View.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
zoomImageFromThumb(thumb1View, R.mipmap.ic_launcher);
}
});
mShortAnimationDuration = getResources().getInteger(android.R.integer.config_longAnimTime);
}
And this is the layout:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="16dp">
<ImageButton
android:id="#+id/thumb_button_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:src="#mipmap/ic_launcher"
android:scaleType="centerCrop"
android:layout_gravity="center"
android:background="#null"
android:contentDescription="#string/description_image_1" />
</LinearLayout>
<!-- This initially-hidden ImageView will hold the expanded/zoomed version of
the images above. Without transformations applied, it takes up the entire
screen. To achieve the "zoom" animation, this view's bounds are animated
from the bounds of the thumbnail button above, to its final laid-out
bounds.
-->
<ImageView
android:id="#+id/expanded_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="100dp"
android:visibility="invisible"
android:contentDescription="#string/description_zoom_touch_close" />
</FrameLayout>
However, I am trying to make it to keep zooming in/out once activity is loaded without need to click to activate it, then on click stop the zooming.
Much appreciated,
I figured it out by using animation xml in res/anim directory. Add new xml called my_alpha_animation.xml in your res/anim directory, if anim does not existe, create it:
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="500"
android:fromXScale="0.1"
android:fromYScale="0.1"
android:interpolator="#android:anim/accelerate_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1.0"
android:toYScale="1.0"
android:repeatCount="infinite"
android:repeatMode="reverse">
</scale>
and loading it on Activities onResume():
#Override
protected void onResume(){
super.onResume();
doAlpha(btnAlpha);
}
And here is the method that does it:
private void doAlpha(View v){
Animation alphaAnimation = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.my_alpha_animation);
image.startAnimation(alphaAnimation); //image is my ImageView
}
Above, "image" is my ImageView for which I set image called "scanweb" from my mipmap directory and clear any animation that might be in progress in my Activity onCreate():
image = (ImageView) findViewById(R.id.imageView);
image.setImageResource(R.mipmap.scanweb);
image.clearAnimation();
The end result is that once Activity is created, my image "scanweb" starts pulsating (zooming in/out) infinitely.
I have:
a recyclerview with GridLayoutManager
grid item layout with an ImageView in it (with height wrap_content and width match_parent) wrapped in a Framelayout, so the image is bottom|center_horizontal aligned
Picasso loading an image into ImageView asynchronously from the web
Current situation:
The image is loaded into the imageView, but the imageview's size is the recycled grid item's imageview's size.
What I would like to achieve:
After loading the image into the imageview, resize the imageview on runtime (redraw).
What I have tried:
notifyItemChanged() - could do the trick (in theory at least), but I
am not able to check if the current grid item's view is in layout
state, so my app crashed with IllegalStateException
listening to Picasso's load with a Callback, and onSuccess() check the imageview drawable aspectratio and try to resize the imageview itself with
requestLayout(). Did not work. (Well it worked, but only when there
is an animation or something triggering the redraw of the layout. If
there is nothing, then the imageview is not redrawn.)
listening to Picasso's load with a Callback, and onSuccess() start an animation animating the alpha of the imageview. This would trigger the redraw. But this sometimes worked sometimes not(and I don't know why).
What I did was putting an ImageView into FrameLayout and then changing this FrameLayout's size to needed. Hope it would help you.
<FrameLayout
android:id="#+id/frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true">
<ImageView
android:id="#+id/video_thumbnail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitStart"
android:adjustViewBounds="true" />
</FrameLayout>
P.S. changing FrameLayout's size:
viewHolder.frame.setMinimumWidth(neededWidth);
Picasso has a function to resize your image. Something like this:
Picasso.with(context)
.load(url)
.resize(50, 50)
.centerCrop()
.into(imageView)
You can change centerCrop, to play with aspect ratio
I wanna show this using a
Campaign Banner List Using Dynamic Width - Height Example
that:
Create an item_campaign_banner.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="wrap_content"
android:background="#android:color/white">
<RelativeLayout
android:id="#+id/campaignImageSuperView"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/campaignImage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:src="#drawable/campaign_image_error_icon"
android:visibility="visible"
android:layout_alignParentTop="true"
android:layout_alignParentStart="true"/>
<View
android:id="#+id/campaignSectionLine"
android:layout_width="match_parent"
android:layout_height="5dp"
android:layout_below="#+id/campaignImage"
android:background="#F2F2F2" />
</RelativeLayout>
</RelativeLayout>
Than create a BannerHolder.java
public class BannerHolder extends RecyclerView.ViewHolder {
private ImageView campaignImage;
private View campaignSectionLine;
private ViewGroup campaignImageSuperView;
public BannerHolder(#NonNull View itemView) {
super(itemView);
campaignImage = (ImageView) itemView.findViewById(R.id.campaignImage);
campaignSectionLine = itemView.findViewById(R.id.campaignSectionLine);
campaignImageSuperView = itemView.findViewById(R.id.campaignImageSuperView);
}
public ImageView getCampaignImage() {
return campaignImage;
}
public ViewGroup getCampaignImageSuperView() {
return campaignImageSuperView;
}
}
Than apply this in onBindViewHolder(#NonNull RecyclerView.ViewHolder incomingHolder, int position) method that
your created YourAdapter.java
if (incomingHolder instanceof BannerHolder) {
BannerHolder bannerHolder = (BannerHolder) incomingHolder;
Banner banner = (Banner) campaigns.get(position);
if(banner != null && banner.getImage() != null && banner.getImage().getWidth() != null && banner.getImage().getHeight() != null) {
Picasso.with(activity)
.load(banner.getImage().getUrl())
.error(R.drawable.campaign_image_error_icon)
.into(bannerHolder.getCampaignImage());
bannerHolder.getCampaignImageSuperView().setMinimumWidth(banner.getImage().getWidth());
bannerHolder.getCampaignImageSuperView().setMinimumHeight(banner.getImage().getHeight());
}
}
That's all!
I've been experimenting with the ripple animation in my latest side project. I'm having some trouble finding an "elegant" solution to using it in certain situations for touch events. Namely with images, especially in list, grid, and recycle views. The animation almost always seems to animate behind the view, not the on top of it. This is a none issue in Buttons and TextViews but if you have a GridView of images, the ripple appears behind or below the actual image. Obviously this is not what I want, and while there are solutions that I consider to be a work around, I'm hoping there is something simpler i'm just unaware of.
I use the following code to achieve a custom grid view with images. I'll give full code CLICK HERE so you can follow along if you choose.
Now just the important stuff. In order to get my image to animate on touch I need this
button_ripple.xml
<ripple
xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#color/cream_background">
<item>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Pressed -->
<item
android:drawable="#color/button_selected"
android:state_pressed="true"/>
<!-- Selected -->
<item
android:drawable="#color/button_selected"
android:state_selected="true"/>
<!-- Focus -->
<item
android:drawable="#color/button_selected"
android:state_focused="true"/>
<!-- Default -->
<item android:drawable="#color/transparent"/>
</selector>
</item>
</ripple>
custom_grid.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="horizontal">
<ImageView
android:id="#+id/sceneGridItem"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/button_ripple"
android:gravity="center_horizontal"/>
</LinearLayout>
activity_main.xml
<GridView
android:id="#+id/sceneGrid"
android:layout_marginTop="15dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:verticalSpacing="15dp"
android:numColumns="5" />
The line where all magic and problems occur is when I set the background. While this does in fact give me a ripple animation on my imageview, it animates behind the imageview. I want the animation to appear on top of the image. So I tried a few different things like
Setting the entire grid background to button_ripple.
<GridView
android:id="#+id/sceneGrid"
android:layout_marginTop="15dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:verticalSpacing="15dp"
android:background="#drawable/button_ripple"
android:numColumns="5" />
It does exactly what you'd think, now the entire grid has a semi transparent background and no matter what image i press the entire grid animates from the center of the grid. While this is kind of cool, its not what I want.
Setting the root/parent background to button_ripple.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:background="#drawable/button_ripple"
android:orientation="horizontal">
The area is now larger and fills the entire cell of the grid (not just the image), however it doesn't bring it to the front.
Changing custom_grid.xml to a RelativeLayout and putting two ImageViews on top of each other
custom_grid.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="#+id/gridItem"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true" />
<ImageView
android:id="#+id/gridItemOverlay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:background="#drawable/button_ripple" />
</RelativeLayout>
CustomGridAdapter.java
....
gridItemOverLay = (ImageView) findViewById(R.id.gridItemOverlay);
gridItemOverlay.bringToFront();
This works. Now the bottom ImageView contains my image, and the top animates, giving the illusion of a ripple animation on top of my image. Honestly though this is a work around. I feel like this is not how it was intended. So I ask you fine people, is there a better way or even a different way?
I liked android developer's answer so I decided to investigate how to do step 2 of his solution in code.
You need to get this piece of code from Jake Wharton here : https://gist.github.com/JakeWharton/0a251d67649305d84e8a
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;
public class ForegroundImageView extends ImageView {
private Drawable foreground;
public ForegroundImageView(Context context) {
this(context, null);
}
public ForegroundImageView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ForegroundImageView);
Drawable foreground = a.getDrawable(R.styleable.ForegroundImageView_android_foreground);
if (foreground != null) {
setForeground(foreground);
}
a.recycle();
}
/**
* Supply a drawable resource that is to be rendered on top of all of the child
* views in the frame layout.
*
* #param drawableResId The drawable resource to be drawn on top of the children.
*/
public void setForegroundResource(int drawableResId) {
setForeground(getContext().getResources().getDrawable(drawableResId));
}
/**
* Supply a Drawable that is to be rendered on top of all of the child
* views in the frame layout.
*
* #param drawable The Drawable to be drawn on top of the children.
*/
public void setForeground(Drawable drawable) {
if (foreground == drawable) {
return;
}
if (foreground != null) {
foreground.setCallback(null);
unscheduleDrawable(foreground);
}
foreground = drawable;
if (drawable != null) {
drawable.setCallback(this);
if (drawable.isStateful()) {
drawable.setState(getDrawableState());
}
}
requestLayout();
invalidate();
}
#Override protected boolean verifyDrawable(Drawable who) {
return super.verifyDrawable(who) || who == foreground;
}
#Override public void jumpDrawablesToCurrentState() {
super.jumpDrawablesToCurrentState();
if (foreground != null) foreground.jumpToCurrentState();
}
#Override protected void drawableStateChanged() {
super.drawableStateChanged();
if (foreground != null && foreground.isStateful()) {
foreground.setState(getDrawableState());
}
}
#Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (foreground != null) {
foreground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight());
invalidate();
}
}
#Override protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (foreground != null) {
foreground.setBounds(0, 0, w, h);
invalidate();
}
}
#Override public void draw(Canvas canvas) {
super.draw(canvas);
if (foreground != null) {
foreground.draw(canvas);
}
}
}
This is the attrs.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ForegroundImageView">
<attr name="android:foreground"/>
</declare-styleable>
</resources>
Now, create your ForegroundImageView like so in your layout.xml:
<com.example.ripples.ForegroundImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
android:foreground="?android:selectableItemBackground"
android:src="#drawable/apples"
android:id="#+id/image" />
The image will now ripple.
Instead of trying to add the ripple to each individual view in the adapter, you can just add it at the GridView level like this:
<GridView
android:id="#+id/gridview"
...
android:drawSelectorOnTop="true"
android:listSelector="#drawable/your_ripple_drawable"/>
Maybe try one of these solutions (got the tip from here) :
Wrap the drawable in a RippleDrawable² before setting it on the ImageView:
Drawable image = …
RippleDrawable rippledImage = new RippleDrawable(
ColorStateList.valueOf(rippleColor), image, null);
imageView.setImageDrawable(rippledImage);
Extend ImageView and add a foreground attribute to it (like FrameLayout has³). See this example⁴ from +Chris Banes of adding it to a LinearLayout. If you do this then make sure you pass through the touch co-ordinates so that the ripple starts from the correct point:
#Override
public void drawableHotspotChanged(float x, float y) {
super.drawableHotspotChanged(x, y);
if (foreground != null) {
foreground.setHotspot(x, y);
}
}
I have an image gallery (built with a RecyclerView and a GridLayoutManager). The image is set using Picasso. To add a ripple to the image I've wrapped the ImageView with a FrameLayout. The item layout is:
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="120dp"
android:foreground="#drawable/ripple"
android:clickable="true"
android:focusable="true"
>
<ImageView
android:id="#+id/grid_item_imageView"
android:layout_width="match_parent"
android:layout_height="120dp"
android:layout_gravity="center"
android:scaleType="centerInside"
/>
</FrameLayout>
Note the android:foreground. It's not the same as android:background. I've tried without android:clickable="true" and android:focusable="true" and it also works, but it doesn't hurt.
Then add a ripple.xml drawable into res/drawable:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="rectangle">
<solid android:color="#7FF5AC8E" />
</shape>
</item>
<item>
<shape android:shape="rectangle">
<solid android:color="#android:color/transparent" />
</shape>
</item>
</selector>
Note that this shows a semi-transparent color on top of the image when the item is selected (for devices < 5.0). You can remove it if you don't want it.
Then add the ripple.xml drawable with ripple into res/drawable-v21:
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#color/coral">
</ripple>
You can use the default ripple effect instead of the custom ripple.xml, but it's really difficult to see on top of an image because it's grey:
android:foreground="?android:attr/selectableItemBackground"
Loading an image that is larger than the width of a Galaxy Tab 2 P5100 (running 4.1.2) into an ImageView adds some sort of top/bottom padding to the loaded image.
Here's a screenshot with Show layout boundaries turned on:
Here's how it should look (from a Nexus 10 running 4.4.2):
The code I use (for both examples above) is
public class ImageBugActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bug);
// This bug is still reproducible if I use the
// Universal-Image-Loader library or if I change the dimensions of
// the image to a different width
loadImage("http://placehold.it/1600x1000", (ImageView)findViewById(R.id.image));
}
private void loadImage(final String url, final ImageView view) {
new Thread(new Runnable() {
#Override
public void run() {
try {
final Bitmap bitmap = BitmapFactory.decodeStream(new URL(url).openConnection().getInputStream());
runOnUiThread(new Runnable() {
#Override
public void run() {
view.setImageBitmap(bitmap);
}
});
} catch (Exception e) {
Log.e("loadImage", e.getMessage(), e);
}
}
}).start();
}
}
And the layout file is
<?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" >
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="On a Galaxy Tab 2 the image below it is pushed to the center of the remaining space." />
<ImageView
android:id="#+id/image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top" />
</LinearLayout>
Does this seem to be an Android/Samsung bug or am I making a dumb mistake?
Setting the android:scaleType of the ImageView to "fitStart" should do the trick.
You should use "match_parent" for layout_height of the ImageView.
<ImageView
android:id="#+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="top" />