How could i highlight Gallery selected item without adding the gray border to the image,
without using this.
TypedArray typArray = obtainStyledAttributes(R.styleable.GalleryTheme);
GalItemBg = typArray.getResourceId(
R.styleable.GalleryTheme_android_galleryItemBackground, 3);
typArray.recycle();
and could i add a reflect to the images,
you can define your own view like this:
a custom background:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle" android:layout_width="wrap_content">
<stroke android:width="1dp" android:color="#FF000000" />
<solid android:color="#00000000" />
<padding android:left="1dp" android:top="1dp" android:right="1dp"
android:bottom="1dp" />
<corners android:radius="1dp" />
</shape>
</item>
<item android:top="1dp" android:bottom="1dp">
<shape android:shape="rectangle">
<gradient android:startColor="#252525" android:endColor="#252525"
android:angle="270" android:centerColor="#545454" />
<!-- border width and color -->
<stroke android:width="1dp" android:color="#FFDDDDDD" />
</shape>
</item>
</layer-list>
its adapter:
public class AdapterGalleryProducts extends ArrayAdapter<String> {
private int ITEM_WIDTH = 136;
private int ITEM_HEIGHT = 88;
private final int mGalleryItemBackground;
private final Context mContext;
private final float mDensity;
public AdapterGalleryProducts(Context context, int resource,
List<String> items) {
super(context, resource, items);
mContext = context;
TypedArray a = mContext
.obtainStyledAttributes(R.styleable.Gallery1);
mGalleryItemBackground = R.drawable.background02;
a.recycle();
mDensity = mContext.getResources().getDisplayMetrics().density;
boInvProducts = new BoInvProducts(mContext);
}
public void setImageSize(int width, int height) {
ITEM_WIDTH = width;
ITEM_HEIGHT = height;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) {
convertView = new ImageView(mContext);
imageView = (ImageView) convertView;
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
imageView.setLayoutParams(new Gallery.LayoutParams(
(int) (ITEM_WIDTH * mDensity + 0.5f),
(int) (ITEM_HEIGHT * mDensity + 0.5f)));
// The preferred Gallery item background
imageView.setBackgroundResource(mGalleryItemBackground);
imageView.setPadding(5, 5, 5, 5);
} else {
imageView = (ImageView) convertView;
}
Bitmap bitmap = null;
try {
bitmap = getBitmapByFilePath(getItem(position));
} catch (Exception e) {
e.printStackTrace();
}
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
imageView.setAdjustViewBounds(true);
}
return imageView;
}
}
and add animation effects to selected item:
gal.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v,
int position, long id) {
gal_onItemClick(parent, v, position, id);
}
});
protected void gal_onItemClick(AdapterView<?> parent, View v,
int position, long id) {
// animate selected image
Animation growAnimation = AnimationUtils.loadAnimation(this,
R.anim.grow_shrink_image);
v.startAnimation(growAnimation);
}
an example of animation (grow_shrink_image.xml):
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200" android:fromXScale="1.0" android:toXScale="1.20"
android:fromYScale="1.0" android:toYScale="1.20" android:pivotX="50%"
android:pivotY="50%" android:interpolator="#android:anim/accelerate_interpolator"
android:fillAfter="false" />
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:startOffset="200" android:duration="200" android:fromXScale="1.0"
android:toXScale="0.8333" android:fromYScale="1.0" android:toYScale="0.8333"
android:pivotX="50%" android:pivotY="50%"
android:interpolator="#android:anim/accelerate_interpolator"
android:fillAfter="false" />
</set>
Related
I want to have a button feedback like Spotify form my Buttons. That means when I click the button the button should be a bit smaller and it should get a light gray ton. That's easy I know but I don't know how to make that with an animation.
Thats the Sample-Button:<Button id="SpotifyButton"/>
I'm looking forward to getting a answer! :)
The hole answer based on the answer from Android Geek.
view_press.xml (in anim res folder)
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/linear_interpolator"
android:fillAfter="true" android:fillBefore="true">
<scale
android:duration="100"
android:fromXScale="1"
android:fromYScale="1"
android:pivotX="70%"
android:pivotY="70%"
android:toXScale="0.95"
android:toYScale="0.95" >
</scale>
</set>
view_release.xml (in anim res folder)
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/linear_interpolator"
android:fillAfter="true" android:fillBefore="true">
<scale
android:duration="100"
android:fromXScale="0.95"
android:fromYScale="0.95"
android:pivotX="70%"
android:pivotY="70%"
android:toXScale="1"
android:toYScale="1">
</scale>
</set>
CustomView
public class SpotifyButton extends AppCompatTextView {
int backgroundColor = ((ColorDrawable) getBackground()).getColor();
GradientDrawable gradientDrawable = getBackgroundShape(backgroundColor);
ValueAnimator defaultAnimator = getDefaultBackgroundAnimator(this);
public SpotifyButton (Context context) {
super(context);
setOnTouchListener();
setBackground(gradientDrawable);
}
public SpotifyButton (Context context, AttributeSet attrs) {
super(context, attrs);
setOnTouchListener();
setBackground(gradientDrawable);
}
public SpotifyButton (Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setOnTouchListener();
setBackground(gradientDrawable);
}
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
#Override
public boolean performClick() {
super.performClick();
return true;
}
public static int manipulateColor(int color, float factor) {
int a = Color.alpha(color);
int r = Math.round(Color.red(color) * factor);
int g = Math.round(Color.green(color) * factor);
int b = Math.round(Color.blue(color) * factor);
return Color.argb(a,
Math.min(r, 255),
Math.min(g, 255),
Math.min(b, 255));
}
public ValueAnimator getDefaultBackgroundAnimator(final View view) {
final float[] from = new float[3],
to = new float[3];
Color.colorToHSV(backgroundColor, from);
Color.colorToHSV(manipulateColor(backgroundColor, 0.7f), to);
ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
anim.setDuration(200);
final float[] hsv = new float[3];
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
// Transition along each axis of HSV (hue, saturation, value)
hsv[0] = from[0] + (to[0] - from[0]) * animation.getAnimatedFraction();
hsv[1] = from[1] + (to[1] - from[1]) * animation.getAnimatedFraction();
hsv[2] = from[2] + (to[2] - from[2]) * animation.getAnimatedFraction();
gradientDrawable.setColor(Color.HSVToColor(hsv));
view.setBackground(gradientDrawable);
}
});
return anim;
}
public GradientDrawable getBackgroundShape(int color) {
GradientDrawable gradientDrawable = new GradientDrawable();
gradientDrawable.setShape(GradientDrawable.RECTANGLE);
gradientDrawable.setCornerRadius(100);
gradientDrawable.setColor(color);
return gradientDrawable;
}
public void setOnTouchListener() {
this.setOnTouchListener(new OnTouchListener() {
#SuppressLint("ClickableViewAccessibility")
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Animation onclick_effect_press = AnimationUtils.loadAnimation(getContext(), R.anim.ui_view_feedback_press);
defaultAnimator.start();
v.startAnimation(onclick_effect_press);
return true;
case MotionEvent.ACTION_UP:
Animation onclick_effect_release = AnimationUtils.loadAnimation(getContext(), R.anim.ui_view_feedback_release);
defaultAnimator.reverse();
v.startAnimation(onclick_effect_release);
if (isMotionEventInsideView(v, event)) {
performClick();
}
return true;
}
return false;
}
});
}
private boolean isMotionEventInsideView(View view, MotionEvent event) {
Rect viewRect = new Rect(
view.getLeft(),
view.getTop(),
view.getRight(),
view.getBottom()
);
return viewRect.contains(
view.getLeft() + (int) event.getX(),
view.getTop() + (int) event.getY()
);
}
}
Resault
Happy coding!!
XML file
<LinearLayout
android:id="#+id/ll_done"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="#drawable/bg_rect"
android:paddingStart="60dp"
android:paddingEnd="60dp"
android:paddingTop="20dp"
android:paddingBottom="20dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Done"
android:textColor="#android:color/white"/>
</LinearLayout>
set background to your layout of button. here background is bg_rect
drawable -- bg_rect is
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#color/colorPrimaryDark">
<item android:id="#android:id/mask">
<shape
android:shape="rectangle">
<solid android:color="#android:color/holo_green_light"/>
<corners android:radius="30dp"/>
</shape>
</item>
<item android:id="#android:id/background">
<shape
android:shape="rectangle">
<solid android:color="#5DA19C"/>
<corners android:radius="30dp"/>
</shape>
</item>
</ripple>
change solid android:color according to your need.
Create anim directory in res file then add animation file in anim folder:
Animation file -- onclick_effect.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/linear_interpolator">
<scale
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200"
android:fromXScale="0.9"
android:fromYScale="0.9"
android:pivotX="70%"
android:pivotY="70%"
android:toXScale="1"
android:toYScale="1" >
</scale>
</set>
Add click on button in java class:
llDone.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Animation onclick_effect = AnimationUtils.loadAnimation(this, R.anim.onclick_effect);
llDone.startAnimation(onclick_effect);
}
});
Change color,animation time according to your need.
I hope its work for you.
I got a NavigationView which I'm trying to set a custom list divider to.
I have made the file "drawer_list_divider.xml":
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"
android:thickness="25dp">
<solid android:color="#color/secondaryBackground"/>
I set it like this: android:theme="#style/NavigationViewTheme"
The style:
<style name="NavigationViewTheme" >
<item name="android:listDivider">#drawable/drawer_list_divider</item>
</style>
The divider gets the color I want, but not the thickness which is not affected. I want the divider to be a rectangle of a different height.. how do I set the height/thickness?
Navigation Drawer layout
<android.support.v4.widget.DrawerLayout
android:id="#+id/drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/appBar">
<!--Content-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="#+id/content_textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="#string/app_name" />
</LinearLayout>
<!-- Navigation Drawer-->
<android.support.v7.widget.RecyclerView
android:id="#+id/drawerRecyclerView"
android:layout_width="300dp"
android:layout_height="match_parent"
android:layout_gravity="left"
android:background="#ffffff">
</android.support.v7.widget.RecyclerView>
Then set a recyclerview adapter
public class DrawerAdapter extends
RecyclerView.Adapter<DrawerAdapter.DrawerViewHolder> {
private ArrayList<DrawerItem> drawerMenuList;
public DrawerAdapter(ArrayList<DrawerItem> drawerMenuList) {
this.drawerMenuList = drawerMenuList;
}
#Override
public DrawerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.menu_item, parent, false);
return new DrawerViewHolder(view);
}
#Override
public void onBindViewHolder(DrawerViewHolder holder, int position) {
holder.title.setText(drawerMenuList.get(position).getTitle());
holder.icon.setImageResource(drawerMenuList.get(position).getIcon());
}
#Override
public int getItemCount() {
return drawerMenuList.size();
}
class DrawerViewHolder extends RecyclerView.ViewHolder {
TextView title;
ImageView icon;
public DrawerViewHolder(View itemView) {
super(itemView);
title = (TextView) itemView.findViewById(R.id.title);
icon = (ImageView) itemView.findViewById(R.id.icon);
} } }
Then set the adapter were you want to show the navigation drawer. While setting
the recyclerview set the divider
DrawerAdapter adapter = new DrawerAdapter(mDrawerItemList);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.addItemDecoration(new SimpleDividerItemDecoration(getActivity()));
mRecyclerView.setAdapter(adapter);
Here is the itemDecorator class
public class SimpleDividerItemDecoration extends RecyclerView.ItemDecoration {
private Drawable mDivider;
public SimpleDividerItemDecoration(Context context) {
mDivider = context.getResources().getDrawable(R.drawable.recycler_horizontal_divider);
}
#Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
int left = parent.getPaddingLeft();
int right = parent.getWidth() - parent.getPaddingRight();
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
int top = child.getBottom() + params.bottomMargin;
int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}}
Here is the divider drawable.
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size
android:width="1dp"
android:height="1dp" />
<solid android:color="#2EC590" />
</shape>
You should use the tag size rather than thickness in the tag shape.
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<size android:height="1dp" />
<solid android:color="#10000000"/>
</shape>
Is there a way how to animate an Icon? I need to apply a "wave" animation to my custom drawable marker on my Google Map. Any suggestions are very well welcomed!
Note.: My plan now is to run a handler worker thread that will constantly call the "setIcon" method and thus animate the icon.
Desired effect:
My icon drawable:
XML:
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid
android:color="#0093e8"/>
<size
android:width="120dp"
android:height="120dp"/>
</shape>
This is my onMapReady method:
#Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
mDotMarkerBitmap = generateBitmapFromDrawable();
markerOptions = new MarkerOptions()
.position(xDesign)
.title("xDesign in Edinburgh")
.icon(BitmapDescriptorFactory.fromBitmap(mDotMarkerBitmap));
mXDesignMarker = mMap.addMarker(markerOptions);
BitmapDescriptor bitmapDescriptor = markerOptions.getIcon();
Animation fadeOut = new AlphaAnimation(1, 0);
fadeOut.setDuration(1000);
AnimationSet animation = new AnimationSet(true);
animation.addAnimation(fadeOut);
// Icons dont have animate method icon.setAnimation(animation);
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(xDesign, 15));
}
My generateBitmapFromDrawable method:
private Bitmap generateBitmapFromDrawable() {
int px = getResources().getDimensionPixelSize(R.dimen.map_dot_marker_size);
Bitmap mDotMarkerBitmap = Bitmap.createBitmap(px, px, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(mDotMarkerBitmap);
Drawable shape = getResources().getDrawable(R.drawable.circle_drawable);
shape.setBounds(0, 0, mDotMarkerBitmap.getWidth(), mDotMarkerBitmap.getHeight());
shape.draw(canvas);
return mDotMarkerBitmap;
}
What I did I used this custom animation class:
public class RadiusAnimation extends Animation {
private GroundOverlay groundOverlay;
private final int startingSize = 100;
public RadiusAnimation(GroundOverlay groundOverlay) {
this.groundOverlay = groundOverlay;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
groundOverlay.setDimensions((startingSize * interpolatedTime));
groundOverlay.setTransparency(interpolatedTime);
}
#Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
}
}
And then in my mapFragment:
public class MapFragment extends Fragment implements OnMapReadyCallback {
private RadiusAnimation groundAnimation;
private AnimationSet breadingAnimations;
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
rippleBackground = new RippleBackground(getActivity());
rippleBackground.setBackgroundColor(ContextCompat.getColor(getActivity(),
R.color.pulseMarker1));
mapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
breadingAnimations = new AnimationSet(false);
}
#Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
//Add smallest image drawable overlay
Bitmap mDotMarkerBitmap1 = generateBitmapFromDrawable(R.drawable.circle_drawable);
final int widthOne = 80;
final int animationDurationOne = 1200;
GroundOverlay groundOverlay1 = mMap.addGroundOverlay(new GroundOverlayOptions()
.image(BitmapDescriptorFactory.fromBitmap(mDotMarkerBitmap1))
.position(xDesign, widthOne));
groundAnimation = new RadiusAnimation(groundOverlay1);
groundAnimation.setRepeatCount(Animation.INFINITE);
groundAnimation.setRepeatMode(Animation.RESTART);
groundAnimation.setDuration(animationDurationOne);
// groundAnimation.setStartOffset(700);
breadingAnimations.addAnimation(groundAnimation);
Bitmap mDotMarkerBitmap2 = generateBitmapFromDrawable(R.drawable.circle_drawable2);
final int widthTwo = 200;
final int animationDurationTwo = 2000;
GroundOverlay groundOverlay2 = mMap.addGroundOverlay(new GroundOverlayOptions()
.image(BitmapDescriptorFactory.fromBitmap(mDotMarkerBitmap2))
.position(xDesign, widthTwo));
Animation groundAnimation2 = new RadiusAnimation(groundOverlay2);
groundAnimation2.setRepeatCount(Animation.INFINITE);
groundAnimation2.setRepeatMode(Animation.RESTART);
groundAnimation2.setDuration(animationDurationTwo);
// groundAnimation2.setStartOffset(1500);
breadingAnimations.addAnimation(groundAnimation2);
Bitmap mDotMarkerBitmap3 = generateBitmapFromDrawable(R.drawable.circle_drawable3);
final int widthThree = 300;
final int animationDurationThree = 3000;
GroundOverlay groundOverlay3 = mMap.addGroundOverlay(new GroundOverlayOptions()
.image(BitmapDescriptorFactory.fromBitmap(mDotMarkerBitmap3))
.position(xDesign, widthThree));
Animation groundAnimation3 = new RadiusAnimation(groundOverlay3);
groundAnimation3.setRepeatCount(Animation.INFINITE);
groundAnimation3.setRepeatMode(Animation.RESTART);
// groundAnimation3.setStartOffset(500);
groundAnimation3.setDuration(animationDurationThree);
breadingAnimations.addAnimation(groundAnimation3);
mapFragment.getView().startAnimation(breadingAnimations); // start animation
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(xDesign, zoomLeval));
}
private void logThis(String message) {
Log.d(TAG, message);
}
#NonNull
private Bitmap generateBitmapFromDrawable(int drawablesRes) {
int px = getResources().getDimensionPixelSize(R.dimen.map_dot_marker_size);
Bitmap mDotMarkerBitmap = Bitmap.createBitmap(px, px, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(mDotMarkerBitmap);
Drawable shape = getResources().getDrawable(drawablesRes);
shape.setBounds(0, 0, mDotMarkerBitmap.getWidth(), mDotMarkerBitmap.getHeight());
shape.draw(canvas);
return mDotMarkerBitmap;
}
}
Hi you can create your desired wave animation in xml itself
circle_drawable.xml
<animation-list android:id="#+id/selected" android:oneshot="false">
<item android:drawable="#drawable/image1" android:duration="50" />
<item android:drawable="#drawable/image2" android:duration="50" />
<item android:drawable="#drawable/image3" android:duration="50" />//you can add more xmls and alter the duration to make the animation look good
</animation-list>
image1.xml
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid
android:color="#color/dark_blue"/>
<size
android:width="60dp"
android:height="60dp"/>
</shape>
image2.xml
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid
android:color="#color/blue"/>
<size
android:width="90dp"
android:height="90dp"/>
</shape>
image3.xml
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid
android:color="#color/blue"/>
<size
android:width="120dp"
android:height="120dp"/>
</shape>
finally add the drawable as marker in your code
private Bitmap generateBitmapFromDrawable() {
int px = getResources().getDimensionPixelSize(R.dimen.map_dot_marker_size);
Bitmap mDotMarkerBitmap = Bitmap.createBitmap(px, px, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(mDotMarkerBitmap);
Drawable shape = getResources().getDrawable(R.drawable.circle_drawable);
shape.setBounds(0, 0, mDotMarkerBitmap.getWidth(), mDotMarkerBitmap.getHeight());
shape.draw(canvas);
// Get the background, which has been compiled to an AnimationDrawable object.
//Edited:
AnimationDrawable frameAnimation = (AnimationDrawable) view.getBackground(); //view is the marker
// Start the animation (looped playback by default).
frameAnimation.start();
return mDotMarkerBitmap;
}
reference https://developer.android.com/reference/android/graphics/drawable/AnimationDrawable.html
I'm trying to figure out how to set up a custom listSelector to my listView. The code is more-or-less copy&paste from solutions that worked for others in stackoverflow, but it doesn't work for me.
When I turn on the CAB mode, then I can select and deselect the items, and I see the number of selected items in the CAB navbar, but I don't see any indication in the list. I would like to see which items are selected. Any idea why it doesn't work?
layout/category_list.xml:
<?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:id="#+id/categories_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ListView
android:id="#+id/category_browser"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:dividerHeight="1dp"
android:listSelector="#drawable/list_selector"
android:drawSelectorOnTop="true"
tools:listitem="#layout/category_list_item" >
</ListView>
<include layout="#layout/ad"/>
</LinearLayout>
layout/category_list_item.xml:
<?xml version="1.0" encoding="utf-8"?>
<com.fletech.smartbaby.api.CheckableLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/category_browser_list_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="fill"
android:clickable="true"
android:gravity="fill"
android:orientation="horizontal">
<ImageView
android:id="#+id/category_item_icon"
android:layout_width="90dp"
android:layout_height="90dp"
android:layout_gravity="fill"
android:layout_weight="0"
android:background="#ffffff"
android:src="#drawable/ic_launcher" />
<TextView
android:id="#+id/category_browser_list_title"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_vertical|start"
android:paddingLeft="10dp"
android:paddingRight="10dp"
tools:text="Category name"
android:textColor="#000000"
android:textSize="24sp" />
<include layout="#layout/category_features"
android:layout_centerInParent="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
/>
<include layout="#layout/category_status"
android:layout_centerInParent="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
/>
</com.fletech.smartbaby.api.CheckableLinearLayout>
drawable/list_selector.xml:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="#40777777"/>
</shape>
</item>
<item android:state_long_pressable="true">
<shape android:shape="rectangle">
<solid android:color="#400000ff"/>
</shape>
</item>
<item android:state_checkable="true">
<shape android:shape="rectangle">
<solid android:color="#400000ff"/>
</shape>
</item>
<item android:state_checked="true">
<shape android:shape="rectangle">
<solid android:color="#800000ff"/>
</shape>
</item>
</selector>
and CheckableLinearLayout.java:
package com.fletech.smartbaby.api;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
//import android.support.v7.widget.LinearLayoutCompat; // do I need this? I can't see any difference
import android.util.AttributeSet;
import android.util.Log;
import android.widget.Checkable;
import android.widget.LinearLayout;
public class CheckableLinearLayout extends LinearLayout implements Checkable {
private static final String TAG = CheckableLinearLayout.class.getSimpleName();
private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked};
private boolean mChecked;
public CheckableLinearLayout(Context context) {
super(context);
}
public CheckableLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
public CheckableLinearLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void setChecked(boolean checked) {
Log.v(TAG, "setChecked: " + checked);
if (mChecked != checked) {
mChecked = checked;
refreshDrawableState();
}
}
#Override
public boolean isChecked() {
return mChecked;
}
#Override
public void toggle() {
Log.v(TAG, "toggle: " + mChecked + " -> " + (!mChecked));
setChecked(!mChecked);
}
#Override
// not sure if needed, as it seems to work without this as well
public boolean performClick() {
Log.v(TAG, "performClick");
toggle();
return super.performClick();
}
#Override
// this might be unnecessary
protected void drawableStateChanged() {
Log.v(TAG, "drawableStateChanged");
super.drawableStateChanged();
final Drawable drawable=getBackground();
if(drawable!=null)
{
final int[] myDrawableState=getDrawableState();
drawable.setState(myDrawableState);
invalidate();
}
}
#Override
protected int[] onCreateDrawableState(int extraSpace) {
Log.v(TAG, "onCreateDrawableState: " + extraSpace);
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (isChecked()) {
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
}
return drawableState;
}
#Override
protected Parcelable onSaveInstanceState() {
SavedState result = new SavedState(super.onSaveInstanceState());
result.checked = mChecked;
return result;
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
setChecked(ss.checked);
}
protected static class SavedState extends BaseSavedState {
protected boolean checked;
protected SavedState(Parcelable superState) {
super(superState);
}
#Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(checked ? 1 : 0);
}
public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
private SavedState(Parcel in) {
super(in);
checked = in.readInt() == 1;
}
}
}
I took me more than a week to find out why this thing that works for everyone doesn't work for me. The answer is simple. I hope it'll help others.
The solution was to change the order of the items in the selector xml. The item without any states has to be the last one:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_long_pressable="true">
<shape android:shape="rectangle">
<solid android:color="#400000ff"/>
</shape>
</item>
<item android:state_checkable="true">
<shape android:shape="rectangle">
<solid android:color="#400000ff"/>
</shape>
</item>
<item android:state_checked="true">
<shape android:shape="rectangle">
<solid android:color="#800000ff"/>
</shape>
</item>
<!-- the item without any states has to be the last: -->
<item>
<shape android:shape="rectangle">
<solid android:color="#40777777"/>
</shape>
</item>
</selector>
The reason is that it seems that the parser looks for the 1st item in the xml file that matches. It means that if you have items with different number of states, then you should always have those that have more states defined in the top of the xml. For example:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checkable="true" android:state_long_pressable="true">
<shape android:shape="rectangle">
<solid android:color="#ffff00"/>
</shape>
</item>
<item android:state_long_pressable="true">
<shape android:shape="rectangle">
<solid android:color="#ff0000"/>
</shape>
</item>
<item android:state_checkable="true">
<shape android:shape="rectangle">
<solid android:color="#00ff00"/>
</shape>
</item>
<item>
<shape android:shape="rectangle">
<solid android:color="#40777777"/>
</shape>
</item>
</selector>
This way, and only this way, you'll see a different color on items that are:
- both checkable and long_pressable
- checkable, but not long_pressable
- long_pressable but not checkable
- everything else
I have customised progressbar.
HorizontalSlider class which inherits from ProgressBar is:
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ProgressBar;
public class HorizontalSlider extends ProgressBar {
private OnProgressChangeListener listener;
private static final int padding = 2;
private static final int getMin = 11;
public interface OnProgressChangeListener {
void onProgressChanged(View v, int progress);
}
public HorizontalSlider(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, android.R.attr.progressBarStyleHorizontal);
}
public HorizontalSlider(Context context, AttributeSet attrs) {
super(context, attrs);
}
public HorizontalSlider(Context context) {
super(context);
}
public void setOnProgressChangeListener(OnProgressChangeListener l) {
listener = l;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE) {
float x_mouse = event.getX() - padding;
float width = getWidth() - 2 * padding;
int progress = Math.round((float) getMax() * (x_mouse / width));
if (progress < getMin)
progress = getMin;
if (progress > getMax())
progress = getMax();
this.setProgress(progress);
if (listener != null)
listener.onProgressChanged(this, progress);
}
return true;
}
}
UI in xml format is:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingLeft="10dip"
android:paddingRight="10dip"
android:paddingTop="100dip"
android:gravity="center_horizontal" >
<com.astro.mania.classes.HorizontalSlider
android:id = "#+id/slider"
android:layout_width = "fill_parent"
android:layout_height= "wrap_content"
android:max="100"
android:layout_centerHorizontal="true"
style="?android:attr/progressBarStyleHorizontal" />
<com.astro.mania.classes.HorizontalSlider
android:id="#+id/progressBar"
android:progressDrawable="#drawable/progressbar"
android:layout_width="fill_parent"
android:layout_height="24dip"
style="?android:attr/progressBarStyleHorizontal"
android:indeterminateOnly="false"
android:max="100"
android:layout_below="#id/slider"
android:layout_marginTop="20dip" />
</RelativeLayout>
Progressbar customised as below:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#android:id/background">
<shape>
<gradient
android:startColor="#fffffe"
android:centerColor="#0b131e"
android:centerY="0.75"
android:endColor="#0d1522"
android:angle="270" />
</shape>
</item>
<item android:id="#android:id/secondaryProgress">
<clip>
<shape>
<gradient
android:startColor="#234"
android:centerColor="#234"
android:centerY="0.75"
android:endColor="#a24"
android:angle="270"
/>
</shape>
</clip>
</item>
<item android:id="#android:id/progress">
<bitmap android:src="#drawable/btn_slide_arrow" android:dither="true" />
</item>
</layer-list>
Finally the activity is like this:
public class LensaMania extends Activity {
private HorizontalSlider slider, slider2;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_PROGRESS);
setContentView(R.layout.activity_lensamania);
Log.i("LensaMania", "LensaMania activity started...");
setProgressBarVisibility(true);
slider = (HorizontalSlider) this.findViewById(R.id.slider);
slider.setOnProgressChangeListener(changeListener);
slider2 = (HorizontalSlider) this.findViewById(R.id.progressBar);
slider2.setVisibility(View.VISIBLE);
slider2.setMax(100);
slider2.setProgress(11);
slider2.setOnProgressChangeListener(changeListener);
}
private OnProgressChangeListener changeListener = new OnProgressChangeListener() {
public void onProgressChanged(View v, int progress) {
setProgress(progress);
Log.i("Progress is:", "=>" + progress);
}
};
}
When I run the application and slide progreebar, the image is repeated like image below. I want to have just one image and when I slide it to left want to see just one arrow. How to ask progressbar to don't repeat the image?