enter code here i am using recyclerview but i need to implement one more
recyclerview inside the adapter layout of the recyclerview. Can
anyone tell me how can i set the json data to the recyclerview
which is inside the cardview of verticsal recycler. Dont tell me
about how to set the horizontal recyclerview i know it very well.
the problem with me is that i am confused that how to set data to
recyclerview which is inside cardview of vertical recyclerview
You can pass a List> to the vertical RecyclerView's adapter and then in the onBindViewHolder, pass the inner list at some index i to the constructor of horizontal RecyclerView's adapter.
For the outer vertical RecyclerView, use this extended class:
public class BetterRecyclerView extends RecyclerView{
private static final int INVALID_POINTER = -1;
private int mScrollPointerId = INVALID_POINTER;
private int mInitialTouchX, mInitialTouchY;
private int mTouchSlop;
public BetterRecyclerView(Context context) {
this(context, null);
}
public BetterRecyclerView(Context context, #Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public BetterRecyclerView(Context context, #Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
final ViewConfiguration vc = ViewConfiguration.get(getContext());
mTouchSlop = vc.getScaledTouchSlop();
}
#Override
public void setScrollingTouchSlop(int slopConstant) {
super.setScrollingTouchSlop(slopConstant);
final ViewConfiguration vc = ViewConfiguration.get(getContext());
switch (slopConstant) {
case TOUCH_SLOP_DEFAULT:
mTouchSlop = vc.getScaledTouchSlop();
break;
case TOUCH_SLOP_PAGING:
mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(vc);
break;
default:
break;
}
}
#Override
public boolean onInterceptTouchEvent(MotionEvent e) {
final int action = MotionEventCompat.getActionMasked(e);
final int actionIndex = MotionEventCompat.getActionIndex(e);
switch (action) {
case MotionEvent.ACTION_DOWN:
mScrollPointerId = MotionEventCompat.getPointerId(e, 0);
mInitialTouchX = (int) (e.getX() + 0.5f);
mInitialTouchY = (int) (e.getY() + 0.5f);
return super.onInterceptTouchEvent(e);
case MotionEventCompat.ACTION_POINTER_DOWN:
mScrollPointerId = MotionEventCompat.getPointerId(e, actionIndex);
mInitialTouchX = (int) (MotionEventCompat.getX(e, actionIndex) + 0.5f);
mInitialTouchY = (int) (MotionEventCompat.getY(e, actionIndex) + 0.5f);
return super.onInterceptTouchEvent(e);
case MotionEvent.ACTION_MOVE: {
final int index = MotionEventCompat.findPointerIndex(e, mScrollPointerId);
if (index < 0) {
return false;
}
final int x = (int) (MotionEventCompat.getX(e, index) + 0.5f);
final int y = (int) (MotionEventCompat.getY(e, index) + 0.5f);
if (getScrollState() != SCROLL_STATE_DRAGGING) {
final int dx = x - mInitialTouchX;
final int dy = y - mInitialTouchY;
final boolean canScrollHorizontally = getLayoutManager().canScrollHorizontally();
final boolean canScrollVertically = getLayoutManager().canScrollVertically();
boolean startScroll = false;
if (canScrollHorizontally && Math.abs(dx) > mTouchSlop && (Math.abs(dx) >= Math.abs(dy) || canScrollVertically)) {
startScroll = true;
}
if (canScrollVertically && Math.abs(dy) > mTouchSlop && (Math.abs(dy) >= Math.abs(dx) || canScrollHorizontally)) {
startScroll = true;
}
return startScroll && super.onInterceptTouchEvent(e);
}
return super.onInterceptTouchEvent(e);
}
default:
return super.onInterceptTouchEvent(e);
}
}
}
For the inner horizontal RecyclerView, use this extended class:
public class FeedRootRecyclerView extends BetterRecyclerView{
public FeedRootRecyclerView(Context context) {
this(context, null);
}
public FeedRootRecyclerView(Context context, #Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public FeedRootRecyclerView(Context context, #Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
/* do nothing */
}
}
You can find proper explanation as to what these classes do over here : http://nerds.headout.com/fix-horizontal-scrolling-in-your-android-app/
Related
I want to implement fast scroll with bubble text view in xamarin.android. I took reference from the following link
https://github.com/xamarin/monodroid-samples/tree/master/FastScroll
This example is not properly working for fewer items. In this example, 5000 items are created for fast scroll and its working properly but in my case, there are only 20 items. The scrollbar cannot be able to scroll end of the page and its fluctuating also.
FastScoller.cs
public class FastScroller : LinearLayout
{
private static int BUBBLE_ANIMATION_DURATION=100;
private static int TRACK_SNAP_RANGE=5;
private TextView bubble;
private View handle;
private RecyclerView recyclerView;
private MyScrollListener scrollListener;
private int height;
private ObjectAnimator currentAnimator = null;
public FastScroller(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
{
init(context);
}
public FastScroller(Context context) : base(context)
{
init(context);
}
public FastScroller(Context context, IAttributeSet attrs) : base(context, attrs)
{
init(context);
}
private void init(Context context)
{
scrollListener = new MyScrollListener(this);
Orientation = Orientation.Horizontal;
SetClipChildren(false);
LayoutInflater inflater = LayoutInflater.FromContext(context);
inflater.Inflate(Resource.Layout.FastScroller, this, true);
bubble = FindViewById<TextView>(Resource.Id.fastscroller_bubble);
handle = FindViewById<View>(Resource.Id.fastscroller_handle);
bubble.Visibility = ViewStates.Invisible;
}
protected override void OnSizeChanged(int w, int h, int oldw, int oldh)
{
base.OnSizeChanged(w, h, oldw, oldh);
height = h;
}
public override bool OnTouchEvent(MotionEvent e)
{
var action = e.Action;
switch (action) {
case MotionEventActions.Down:
if (e.GetX() < handle.GetX())
return false;
if (currentAnimator != null)
currentAnimator.Cancel();
if (bubble.Visibility == ViewStates.Invisible)
showBubble();
handle.Selected = true;
setPosition(e.GetY());
setRecyclerViewPosition(e.GetY());
return true;
case MotionEventActions.Move:
setPosition(e.GetY());
setRecyclerViewPosition(e.GetY());
return true;
case MotionEventActions.Up:
case MotionEventActions.Cancel:
handle.Selected = false;
hideBubble();
return true;
}
return base.OnTouchEvent(e);
}
public void SetRecyclerView(RecyclerView rv)
{
this.recyclerView = rv;
this.recyclerView.SetOnScrollListener(scrollListener);
}
private void setRecyclerViewPosition(float y)
{
if (recyclerView != null) {
var itemCount = recyclerView.GetAdapter().ItemCount;
float proportion;
if ((int)handle.GetY() == 0)
proportion = 0f;
else if (handle.GetY() + handle.Height >= height - TRACK_SNAP_RANGE)
proportion = 1f;
else
proportion = y / (float)height;
int targetPos = getValueInRange(0, itemCount - 1, (int)(proportion * (float)itemCount));
recyclerView.ScrollToPosition(targetPos);
var adapter = recyclerView.GetAdapter() as BaseRecyclerAdapter;
bubble.Text = adapter.GetTextToShowInBubble(targetPos);
}
}
private int getValueInRange(int min,int max,int value)
{
int minimum=Math.Max(min,value);
return Math.Min(minimum,max);
}
private void setPosition(float y)
{
int bubbleHeight=bubble.Height;
int handleHeight=handle.Height;
handle.SetY(getValueInRange(0,height-handleHeight,(int)(y-handleHeight/2)));
bubble.SetY(getValueInRange(0,height-bubbleHeight-handleHeight/2,(int)(y-bubbleHeight)));
}
private void showBubble()
{
bubble.Visibility = ViewStates.Visible;
if(currentAnimator!=null)
currentAnimator.Cancel();
currentAnimator = (ObjectAnimator)ObjectAnimator.OfFloat(bubble, "alpha", 0f, 1f).SetDuration(BUBBLE_ANIMATION_DURATION);
currentAnimator.Start();
}
private void hideBubble()
{
if(currentAnimator != null)
currentAnimator.Cancel();
currentAnimator = (ObjectAnimator)ObjectAnimator.OfFloat(bubble,"alpha",1f,0f).SetDuration(BUBBLE_ANIMATION_DURATION);
currentAnimator.AddListener(new MyListener(this));
currentAnimator.Start();
}
internal class MyListener : AnimatorListenerAdapter
{
private FastScroller scroll;
public MyListener(FastScroller scroller)
{
this.scroll = scroller;
}
public override void OnAnimationEnd(Animator animation)
{
base.OnAnimationEnd(animation);
scroll.bubble.Visibility = ViewStates.Invisible;
scroll.currentAnimator = null;
}
public override void OnAnimationCancel(Animator animation)
{
base.OnAnimationCancel(animation);
scroll.bubble.Visibility = ViewStates.Invisible;
scroll.currentAnimator = null;
}
}
internal class MyScrollListener : RecyclerView.OnScrollListener
{
private readonly FastScroller scroll;
public MyScrollListener(FastScroller scroller)
{
this.scroll = scroller;
}
public override void OnScrolled(RecyclerView recyclerView, int dx, int dy)
{
View firstVisibleView = recyclerView.GetChildAt(0);
int firstVisiblePosition = recyclerView.GetChildPosition(firstVisibleView);
int visibleRange = recyclerView.ChildCount;
int lastVisiblePosition = firstVisiblePosition + visibleRange;
int itemCount = recyclerView.GetAdapter().ItemCount;
int position;
if(firstVisiblePosition==0)
position=0;
else if(lastVisiblePosition==itemCount-1)
position = itemCount-1;
else
position = firstVisiblePosition;
float proportion=(float)position/(float)itemCount;
this.scroll.setPosition(scroll.height*proportion);
}
}
}
Some calculations are faulty in above class.
How to adjust scroll bar properly ?
I'm coding a ring menu that is going to put together several different types of view, which will have all the same size. The thing is that it works perfectly with native views, like ImageView, but when I try to put a custom labeled image view, it simply doesn't appear int the custom ViewGroup. It's also worth mentioning that when this view is declared in the XML file, outside de custom ViewGroup it is shown just fine, but as soon as I put it inside the ViewGroup, or declare it programatically, it vanishes. My guess is that I'm doing something weong inside the onLayout method, but I can't put my finger on it, since all coordinates and view sizes are correct according to the Log.
The XML file for the CompoundView:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:focusable="true">
<ImageView
android:id="#+id/header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:src="#drawable/ropeiconselector"/>
<TextView
android:id="#+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="WWWWWWWWWWWW"
android:background="#color/button"
android:layout_marginLeft="-10dp"
android:layout_centerVertical="true"
android:layout_toRightOf="#id/header"
android:padding="8dp"
/>
The code for the CompoundView (I omitted some unimportant methods)
public class CircularLabeledImageView extends RelativeLayout implements View.OnClickListener {
ImageView headerView;
TextView labelView;
boolean isOpen = false;
String[] itemDesc;
int position;
int size;
int maxLabelWidth = 100;
int minLabelWidth = 20;
final Handler timeHandler = new Handler();
Runnable toggleTimer;
public CircularLabeledImageView(Context context) {
super(context);
//EDIT Methhod called
initView(context);
}
public CircularLabeledImageView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public CircularLabeledImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
private void initView(Context context){
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.expandabletextimageview, this);
headerView = (ImageView) this.findViewById(R.id.header);
labelView = (TextView) this.findViewById(R.id.label);
labelView.setBackgroundResource(R.color.backgroundMenuContents);
headerView.setOnClickListener(this);
itemDesc = new String[]{"Item A","Item B", "Item C","Quantos itens"};
size = itemDesc.length;
toggleTimer = new Runnable() {
#Override
public void run() {
toggle();
}
};
this.setOnFocusChangeListener(new OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (isOpen) {
toggle();
}
}
});
}
}
The code for the custom ViewGroup
public class RingListMenu extends ViewGroup implements View.OnClickListener {
boolean isOpen = true;
int headerSize= 90;
int childSize= 80;
int radiusInit = 150;
int childInitSize = 80;
int radius = 150;
int padding;
DPoint center = new DPoint();
float startingAngle= 2;
DPoint click0;
DPoint clickIni;
DPoint clickFinal;
final static float SENSIBILITY = 10f;
final static float FRICTION = 0.00001f;
final static float MAXVELOCITY = 0.06f;
final static long TOGGLE_DURATION = 300;
private VelocityTracker vTracker = null;
boolean isScrolling;
ImageView circleView;
OnItemClickListener listener = null;
public RingListMenu(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
if (android.os.Build.VERSION.SDK_INT >= 11)
{
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
TypedArray at = context.obtainStyledAttributes(attrs,R.styleable.RingListMenu);
childInitSize = childSize = at.getDimensionPixelSize(R.styleable.RingListMenu_childSize, 0);
radiusInit = radius = at.getDimensionPixelSize(R.styleable.RingListMenu_circleRadius, 0);
headerSize = at.getDimensionPixelSize(R.styleable.RingListMenu_headerSize, 0);
padding = at.getDimensionPixelSize(R.styleable.RingListMenu_padding, 0);
}
public RingListMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
if (android.os.Build.VERSION.SDK_INT >= 11)
{
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
TypedArray at = context.obtainStyledAttributes(attrs,R.styleable.RingListMenu);
childInitSize = childSize = at.getDimensionPixelSize(R.styleable.RingListMenu_childSize, 0);
radiusInit = radius = at.getDimensionPixelSize(R.styleable.RingListMenu_circleRadius, 0);
headerSize = at.getDimensionPixelSize(R.styleable.RingListMenu_headerSize, 0);
padding = at.getDimensionPixelSize(R.styleable.RingListMenu_padding, 0);
}
public RingListMenu(Context context, AttributeSet attrs) {
super(context, attrs);
if (android.os.Build.VERSION.SDK_INT >= 11)
{
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
TypedArray at = context.obtainStyledAttributes(attrs, R.styleable.RingListMenu);
childInitSize = childSize = at.getDimensionPixelSize(R.styleable.RingListMenu_childSize, 0);
radiusInit = radius = at.getDimensionPixelSize(R.styleable.RingListMenu_circleRadius, 0);
headerSize = at.getDimensionPixelSize(R.styleable.RingListMenu_headerSize, 0);
padding = at.getDimensionPixelSize(R.styleable.RingListMenu_padding, 0);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
Log.d("RingList", "Calling Ring list onLayout" + childSize + " " + radius + " " + headerSize);
float angle = (float) (2*Math.PI)/(getChildCount()-1);
center.x = padding+headerSize/2;
center.y = padding+headerSize/2;
float childX;
float childY;
//radius = (float) (getChildCount()*(minSpacing+2*childSize)/(2*Math.PI));
for (int i = 1; i < getChildCount(); i++) {
View child = getChildAt(i);
childX = (float) (center.x + radius * Math.cos(startingAngle + i * angle));
childY = (float) (center.y + radius * Math.sin(startingAngle + i * angle));
child.layout((int) (childX - childSize / 2), (int) (childY - childSize / 2),
(int) (childX + childSize / 2), (int) (childY + childSize / 2));
}
View header = getChildAt(0);
header.setX(padding);
header.setY(padding);
header.layout(padding, padding, padding + headerSize, padding + headerSize);
}
#Override
public void addView(View child) {
child.setTag(getChildCount());
super.addView(child);
child.setOnClickListener(this);
}
}
And finally, the declaration part:
RingListMenu ring = (RingListMenu) findViewById(R.id.ring);
CircularLabeledImageView ViewA = new CircularLabeledImageView(this);
ring.addView(ViewA);
I need to display an array of dots (ImageView) that behave like the RatingBar, here's an example:
This is pretty much an RatingBar rotated, but I've a problem, this application is pixel-perfect and therefore I need to add some margin between the dots. This cannot be done with an RatingBar. With all this issues that I'm facing trying to use the RatingBar I gave up and I decided to make my own component, so far this is the component:
public class DotContainerView extends LinearLayout {
#InjectView(R.id.view_dot_container)
LinearLayout vDotContainer;
private OnRatingBarChangeListener mListener;
public DotContainerView(Context context) {
super(context);
initialize();
}
public DotContainerView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
public DotContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize();
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public DotContainerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initialize();
}
private void initialize() {
View root = LayoutInflater.from(getContext()).inflate(R.layout.view_dot_container, this);
ButterKnife.inject(this, root);
setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
v.onTouchEvent(event);
return false;
}
});
}
public void setRating(int rating) {
for (int index = 0; index < rating; index++) {
vDotContainer.getChildAt(index).setSelected(false);
}
for (int index = rating; index < 10; index++) {
vDotContainer.getChildAt(index).setSelected(true);
}
}
public void setOnRatingBarChangeListener(DotContainerView.OnRatingBarChangeListener listener) {
mListener = listener;
}
//region OnTouch
#OnTouch(R.id.fragment_brightness_control_dot_1)
public boolean onDot1Touched() {
setRating(1);
mListener.onRatingChanged(this, 1, true);
return true;
}
#OnTouch(R.id.fragment_brightness_control_dot_2)
public boolean onDot2Touched() {
setRating(2);
mListener.onRatingChanged(this, 2, true);
return true;
}
#OnTouch(R.id.fragment_brightness_control_dot_3)
public boolean onDot3Touched() {
setRating(3);
mListener.onRatingChanged(this, 3, true);
return true;
}
#OnTouch(R.id.fragment_brightness_control_dot_4)
public boolean onDot4Touched() {
setRating(4);
mListener.onRatingChanged(this, 4, true);
return true;
}
#OnTouch(R.id.fragment_brightness_control_dot_5)
public boolean onDot5Touched() {
setRating(5);
mListener.onRatingChanged(this, 5, true);
return true;
}
#OnTouch(R.id.fragment_brightness_control_dot_6)
public boolean onDot6Touched() {
setRating(6);
mListener.onRatingChanged(this, 6, true);
return true;
}
#OnTouch(R.id.fragment_brightness_control_dot_7)
public boolean onDot7Touched() {
setRating(7);
mListener.onRatingChanged(this, 7, true);
return true;
}
#OnTouch(R.id.fragment_brightness_control_dot_8)
public boolean onDot8Touched() {
setRating(8);
mListener.onRatingChanged(this, 8, true);
return true;
}
#OnTouch(R.id.fragment_brightness_control_dot_9)
public boolean onDot9Touched() {
setRating(9);
mListener.onRatingChanged(this, 9, true);
return true;
}
#OnTouch(R.id.fragment_brightness_control_dot_10)
public boolean onDot10Touched() {
setRating(10);
mListener.onRatingChanged(this, 10, true);
return true;
}
//endregion
public interface OnRatingBarChangeListener {
public void onRatingChanged(DotContainerView ratingBar, float value, boolean fromUser);
}
}
This code works fine, if I tap in a dot all the previous dots'll get selected. The only issue with this is that if I drag my finger across the dots, they don't react as in a RatingBar, only if I tap in each dot. Any idea of how solve this?. And please avoid telling me "Use the RatingBar".
I ended up doing something like this:
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import butterknife.ButterKnife;
import butterknife.InjectView;
/**
* #author astinx
* #since 0.2
* <p>
* Simple widget that shows an array of dots which can be tapped like a {#link android.widget.RatingBar}
*/
public class DotContainerView extends LinearLayout implements View.OnTouchListener {
#InjectView(R.id.view_dot_container)
LinearLayout vDotContainer;
private OnRatingBarChangeListener mListener;
public DotContainerView(Context context) {
super(context);
initialize();
}
public DotContainerView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
public DotContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize();
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public DotContainerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initialize();
}
private void initialize() {
View root = LayoutInflater.from(getContext()).inflate(R.layout.view_dot_container, this);
ButterKnife.inject(this, root);
vDotContainer.setOnTouchListener(this);
}
public void setRating(int rating) {
//If the rating = 5
for (int index = 0; index < rating; index++) {
//This sets the children 10, 9, 8, 5...
vDotContainer.getChildAt(Math.abs(index - 10) - 1).setSelected(true);
}
for (int index = rating; index < 10; index++) {
//Ant this sets the children 6, 7, 8, 9, 10
vDotContainer.getChildAt(Math.abs(index - 10) - 1).setSelected(false);
}
}
public void setOnRatingBarChangeListener(DotContainerView.OnRatingBarChangeListener listener) {
mListener = listener;
}
#Override
public boolean onTouch(View v, MotionEvent event) {
float rawX = event.getX();
float rawY = event.getY();
setRating(rawX, rawY);
return true;
}
#Override
public boolean onInterceptHoverEvent(MotionEvent event) {
float rawX = event.getX();
float rawY = event.getY();
setRating(rawX, rawY);
return super.onInterceptHoverEvent(event);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
float rawX = event.getX();
float rawY = event.getY();
setRating(rawX, rawY);
return false;
}
protected void setRating(float rawX, float rawY) {
Log.d("DotContainer", "x=" + rawX + ";y=" + rawY);
int dotIndexByCoords = 10 - findDotIndexByCoords(rawX, rawY);
setRating(dotIndexByCoords);
mListener.onRatingChanged(this, dotIndexByCoords, true);
}
private View findViewByIndex(int childIndex) {
return vDotContainer.getChildAt(childIndex);
}
/**
* Iterates all over the {#link LinearLayout} searching for the closest child to x,y
* #param x The x axis
* #param y The y axis
* #return The index of the child, -1 if isn't found.
*/
private int findDotIndexByCoords(float x, float y) {
for (int childIndex = 0; childIndex < vDotContainer.getChildCount(); childIndex++) {
float y1 = vDotContainer.getChildAt(childIndex).getY();
float y2 = vDotContainer.getChildAt(childIndex + 1).getY();
if (y1 <= y && y <= y2) {
Log.d("DotContainer", "Child no "+ childIndex);
return childIndex;
}
}
return -1;
}
public interface OnRatingBarChangeListener {
public void onRatingChanged(DotContainerView ratingBar, float value, boolean fromUser);
}
}
I know that're things that can be improved, but for those who want to do something quick withouth yelling and cursing at the RatingBar this is something quick, that gets the job done. Basically is just a LinearLayout that contains an array of ImageView each one has a drawable selector that changes the drawable whether is pressed or not. This class overrides the methodn onInterceptTouchEvent and returns false so it's continiously called, inside this method we check which dot was clicked.
I this specific example I want set hint text color progrommatically but I couldn't change it.
public class FloatingHintEditText extends EditText {
private static enum Animation { NONE, SHRINK, GROW }
private final Paint mFloatingHintPaint = new Paint();
private final ColorStateList mHintColors;
private final float mHintScale;
private final int mAnimationSteps;
private boolean mWasEmpty;
private int mAnimationFrame;
private Animation mAnimation = Animation.NONE;
public FloatingHintEditText(Context context) {
this(context, null);
}
public FloatingHintEditText(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.floatingHintEditTextStyle);
}
public FloatingHintEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedValue typedValue = new TypedValue();
getResources().getValue(R.dimen.floatinghintedittext_hint_scale, typedValue, true);
mHintScale = typedValue.getFloat();
mAnimationSteps = getResources().getInteger(R.dimen.floatinghintedittext_animation_steps);
mHintColors = getHintTextColors();
mWasEmpty = TextUtils.isEmpty(getText());
}
#Override
public int getCompoundPaddingTop() {
final FontMetricsInt metrics = getPaint().getFontMetricsInt();
final int floatingHintHeight = (int) ((metrics.bottom - metrics.top) * mHintScale);
return super.getCompoundPaddingTop() + floatingHintHeight;
}
#Override
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
super.onTextChanged(text, start, lengthBefore, lengthAfter);
final boolean isEmpty = TextUtils.isEmpty(getText());
// The empty state hasn't changed, so the hint stays the same.
if (mWasEmpty == isEmpty) {
return;
}
mWasEmpty = isEmpty;
// Don't animate if we aren't visible.
if (!isShown()) {
return;
}
if (isEmpty) {
mAnimation = Animation.GROW;
setHintTextColor(Color.TRANSPARENT);
} else {
mAnimation = Animation.SHRINK;
}
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (TextUtils.isEmpty(getHint())) {
return;
}
final boolean isAnimating = mAnimation != Animation.NONE;
// The large hint is drawn by Android, so do nothing.
if (!isAnimating && TextUtils.isEmpty(getText())) {
return;
}
mFloatingHintPaint.set(getPaint());
mFloatingHintPaint.setColor(
mHintColors.getColorForState(getDrawableState(), mHintColors.getDefaultColor()));
final float hintPosX = getCompoundPaddingLeft() + getScrollX();
final float normalHintPosY = getBaseline();
final float floatingHintPosY = normalHintPosY + getPaint().getFontMetricsInt().top + getScrollY();
final float normalHintSize = getTextSize();
final float floatingHintSize = normalHintSize * mHintScale;
// If we're not animating, we're showing the floating hint, so draw it and bail.
if (!isAnimating) {
mFloatingHintPaint.setTextSize(floatingHintSize);
canvas.drawText(getHint().toString(), hintPosX, floatingHintPosY, mFloatingHintPaint);
return;
}
if (mAnimation == Animation.SHRINK) {
drawAnimationFrame(canvas, normalHintSize, floatingHintSize,
hintPosX, normalHintPosY, floatingHintPosY);
} else {
drawAnimationFrame(canvas, floatingHintSize, normalHintSize,
hintPosX, floatingHintPosY, normalHintPosY);
}
mAnimationFrame++;
if (mAnimationFrame == mAnimationSteps) {
if (mAnimation == Animation.GROW) {
setHintTextColor(mHintColors);
}
mAnimation = Animation.NONE;
mAnimationFrame = 0;
}
invalidate();
}
private void drawAnimationFrame(Canvas canvas, float fromSize, float toSize,
float hintPosX, float fromY, float toY) {
final float textSize = lerp(fromSize, toSize);
final float hintPosY = lerp(fromY, toY);
mFloatingHintPaint.setTextSize(textSize);
canvas.drawText(getHint().toString(), hintPosX, hintPosY, mFloatingHintPaint);
}
private float lerp(float from, float to) {
final float alpha = (float) mAnimationFrame / (mAnimationSteps - 1);
return from * (1 - alpha) + to * alpha;
}
}
So when I call from my activity mEditText.setHintTextColor(getResources().getColor(R.color.red)) the color doesn't change
The color changes when I set only from xml.
I tried to do mEditText.invalidate() but it doesn't help too.
What should I do here to make my hintTextColor red.
Make your fields not final and try this:
public FloatingHintEditText(Context context) {
super(context);
init();
}
public FloatingHintEditText(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public FloatingHintEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
TypedValue typedValue = new TypedValue();
getResources().getValue(R.dimen.floatinghintedittext_hint_scale, typedValue, true);
mHintScale = typedValue.getFloat();
mAnimationSteps = getResources().getInteger(R.dimen.floatinghintedittext_animation_steps);
mHintColors = getHintTextColors();
mWasEmpty = TextUtils.isEmpty(getText());
}
I am looking for any good solution to make parallax with two Views.
My first view is Fragment and second is ListView.
Fragment have min height equals 200dp and when I want drag listview to down the fragment will grow.
I wrote some code, but it isn't all I need.
public class MyListView extends ListView implements OnTouchListener {
int touchActionDownY, touchActionMoveY;
int parentHeight;
int initialPosition = -1;
TextView viewToMove;
LinearLayout.LayoutParams params;
public MyListView(Context context) {
super(context);
init();
}
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MyListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init () {
setOnTouchListener(this);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
parentHeight = MeasureSpec.getSize(heightMeasureSpec);
initialPosition = (int) getY();
}
#Override
public boolean onTouch(View v, MotionEvent event) {
int deltaY = 0;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touchActionDownY = (int)event.getY();
touchActionMoveY = touchActionDownY;
break;
case MotionEvent.ACTION_POINTER_UP:
break;
case MotionEvent.ACTION_MOVE:
deltaY = (int)event.getY() - touchActionMoveY;
touchActionMoveY = (int)event.getY();
if(getFirstVisiblePosition() == 0 && v.getY() < parentHeight && touchActionMoveY > touchActionDownY && deltaY > 0){
params.height = params.height + deltaY;
viewToMove.setLayoutParams(params);
return true;
} else if(v.getY() > 200 && touchActionMoveY < touchActionDownY && deltaY < 0) {
params.height = params.height + deltaY;;
viewToMove.setLayoutParams(params);
return true;
} else {
}
break;
}
return false;
}
public void setViewToMove(TextView v) {
viewToMove = v;
params = (android.widget.LinearLayout.LayoutParams) viewToMove.getLayoutParams();
}
}
In this ListView I need smooth view scroll when user throw list. (View stoped, because method work with ACTION_MOVE).
Any help?
I need interaction with my first fragment.
Parallax should work when only list is draged.