i have made a tab view, i want to change the background color of the tabs, but when i set the color on the tab textview background, the strip disappears.
SlidingTabLayout.java
package com.towntrot.checkin;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Build;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.HorizontalScrollView;
import android.widget.TextView;
import android.widget.Toast;
public class SlidingTabLayout extends HorizontalScrollView {
public interface TabColorizer {
int getIndicatorColor(int position);
int getDividerColor(int position);
}
private static final int TITLE_OFFSET_DIPS = 24;
private static final int TAB_VIEW_PADDING_DIPS = 16;
private static final int TAB_VIEW_TEXT_SIZE_SP = 12;
private int mTitleOffset;
private int mTabViewLayoutId;
private int mTabViewTextViewId;
private ViewPager mViewPager;
private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;
private final SlidingTabStrip mTabStrip;
public SlidingTabLayout(Context context) {
this(context, null);
}
public SlidingTabLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setHorizontalScrollBarEnabled(false);
setFillViewport(true);
mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);
mTabStrip = new SlidingTabStrip(context);
addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
}
public void setViewPager(ViewPager viewPager) {
mTabStrip.removeAllViews();
mViewPager = viewPager;
if (viewPager != null) {
viewPager.setOnPageChangeListener(new InternalViewPagerListener());
populateTabStrip();
}
}
protected TextView createDefaultTabView(Context context) {
TextView textView = new TextView(context);
textView.setGravity(Gravity.CENTER);
textView.setTextColor(getResources().getColor(android.R.color.white));
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);
textView.setTypeface(Typeface.DEFAULT_BOLD);
textView.setBackgroundColor(getResources().getColor(android.R.color.holo_orange_dark));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
TypedValue outValue = new TypedValue();
getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
outValue, true);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
// If we're running on ICS or newer, enable all-caps to match the Action Bar tab style
textView.setAllCaps(true);
}
int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density);
textView.setPadding(padding, padding, padding, padding);
return textView;
}
private void populateTabStrip() {
final PagerAdapter adapter = mViewPager.getAdapter();
final View.OnClickListener tabClickListener = new TabClickListener();
for (int i = 0; i < adapter.getCount(); i++) {
View tabView = null;
TextView tabTitleView = null;
if (mTabViewLayoutId != 0) {
// If there is a custom tab view layout id set, try and inflate it
tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
false);
tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
}
if (tabView == null) {
tabView = createDefaultTabView(getContext());
}
if (tabTitleView == null && TextView.class.isInstance(tabView)) {
tabTitleView = (TextView) tabView;
}
tabTitleView.setText(adapter.getPageTitle(i));
tabView.setOnClickListener(tabClickListener);
mTabStrip.addView(tabView);
}
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (mViewPager != null) {
scrollToTab(mViewPager.getCurrentItem(), 0);
}
}
private void scrollToTab(int tabIndex, int positionOffset) {
final int tabStripChildCount = mTabStrip.getChildCount();
if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
return;
}
View selectedChild = mTabStrip.getChildAt(tabIndex);
if (selectedChild != null) {
int targetScrollX = selectedChild.getLeft() + positionOffset;
if (tabIndex > 0 || positionOffset > 0) {
// If we're not at the first child and are mid-scroll, make sure we obey the offset
targetScrollX -= mTitleOffset;
}
scrollTo(targetScrollX, 0);
}
}
private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
private int mScrollState;
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
int tabStripChildCount = mTabStrip.getChildCount();
if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
return;
}
mTabStrip.onViewPagerPageChanged(position, positionOffset);
View selectedTitle = mTabStrip.getChildAt(position);
int extraOffset = (selectedTitle != null)
? (int) (positionOffset * selectedTitle.getWidth())
: 0;
scrollToTab(position, extraOffset);
if (mViewPagerPageChangeListener != null) {
mViewPagerPageChangeListener.onPageScrolled(position, positionOffset,
positionOffsetPixels);
}
}
#Override
public void onPageScrollStateChanged(int state) {
mScrollState = state;
if (mViewPagerPageChangeListener != null) {
mViewPagerPageChangeListener.onPageScrollStateChanged(state);
}
}
#Override
public void onPageSelected(int position) {
if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
mTabStrip.onViewPagerPageChanged(position, 0f);
scrollToTab(position, 0);
}
if (mViewPagerPageChangeListener != null) {
mViewPagerPageChangeListener.onPageSelected(position);
}
}
}
private class TabClickListener implements View.OnClickListener {
#Override
public void onClick(View v) {
for (int i = 0; i < mTabStrip.getChildCount(); i++) {
if (v == mTabStrip.getChildAt(i)) {
mViewPager.setCurrentItem(i);
return;
}
}
}
}
}
SlidingTabStrip.java
package com.towntrot.checkin;
import android.R;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.widget.LinearLayout;
class SlidingTabStrip extends LinearLayout {
private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2;
private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 4;
private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xffee7600;
private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1;
private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20;
private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f;
private final int mBottomBorderThickness;
private final Paint mBottomBorderPaint;
private final int mSelectedIndicatorThickness;
private final Paint mSelectedIndicatorPaint;
private final int mDefaultBottomBorderColor;
private final Paint mDividerPaint;
private final float mDividerHeight;
private int mSelectedPosition;
private float mSelectionOffset;
private SlidingTabLayout.TabColorizer mCustomTabColorizer;
private final SimpleTabColorizer mDefaultTabColorizer;
SlidingTabStrip(Context context) {
this(context, null);
}
SlidingTabStrip(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
final float density = getResources().getDisplayMetrics().density;
TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true);
final int themeForegroundColor = getResources().getColor(android.R.color.holo_orange_dark);
mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor,
DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);
mDefaultTabColorizer = new SimpleTabColorizer();
mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);
mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor,
DEFAULT_DIVIDER_COLOR_ALPHA));
mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
mBottomBorderPaint = new Paint();
mBottomBorderPaint.setColor(mDefaultBottomBorderColor);
mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
mSelectedIndicatorPaint = new Paint();
mDividerHeight = DEFAULT_DIVIDER_HEIGHT;
mDividerPaint = new Paint();
mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density));
}
void onViewPagerPageChanged(int position, float positionOffset) {
mSelectedPosition = position;
mSelectionOffset = positionOffset;
invalidate();
}
#Override
protected void onDraw(Canvas canvas) {
final int height = getHeight();
final int childCount = getChildCount();
final int dividerHeightPx =0;
final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null
? mCustomTabColorizer
: mDefaultTabColorizer;
// Thick colored underline below the current selection
if (childCount > 0) {
View selectedTitle = getChildAt(mSelectedPosition);
int left = selectedTitle.getLeft();
int right = selectedTitle.getRight();
int color = tabColorizer.getIndicatorColor(mSelectedPosition);
if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);
if (color != nextColor) {
color = blendColors(nextColor, color, mSelectionOffset);
}
// Draw the selection partway between the tabs
View nextTitle = getChildAt(mSelectedPosition + 1);
left = (int) (mSelectionOffset * nextTitle.getLeft() +
(1.0f - mSelectionOffset) * left);
right = (int) (mSelectionOffset * nextTitle.getRight() +
(1.0f - mSelectionOffset) * right);
}
mSelectedIndicatorPaint.setColor(color);
canvas.drawRect(left, height - mSelectedIndicatorThickness, right,
height, mSelectedIndicatorPaint);
}
// Thin underline along the entire bottom edge
canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
// Vertical separators between the titles
int separatorTop = (height - dividerHeightPx) / 2;
for (int i = 0; i < childCount - 1; i++) {
View child = getChildAt(i);
mDividerPaint.setColor(tabColorizer.getDividerColor(i));
canvas.drawLine(child.getRight(), separatorTop, child.getRight(),
separatorTop + dividerHeightPx, mDividerPaint);
}
}
private static int setColorAlpha(int color, byte alpha) {
return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
}
private static int blendColors(int color1, int color2, float ratio) {
final float inverseRation = 1f - ratio;
float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
return Color.rgb((int) r, (int) g, (int) b);
}
private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer {
private int[] mIndicatorColors;
private int[] mDividerColors;
#Override
public final int getIndicatorColor(int position) {
return mIndicatorColors[position % mIndicatorColors.length];
}
#Override
public final int getDividerColor(int position) {
return mDividerColors[position % mDividerColors.length];
}
void setIndicatorColors(int... colors) {
mIndicatorColors = colors;
}
void setDividerColors(int... colors) {
mDividerColors = colors;
}
}
}
In createDefaultTabView i have added:
textView.setBackgroundColor(getResources().getColor(android.R.color.holo_orange_dark));
I am new to android, any help is appreciated, Thank you.
When you set background of textview, it covers yours SlidingTabStrip layout (container of tabs), which is below in your view hierarchy, so bottom strips are covered by TextView background.
To change background color of all of tabs, you should add drawing of background to SlidingTabStrip.onDraw method. To change background of all of the tabs, you can e.g. fill whole tabs container using canvas.drawColor(int color) method
#Override
protected void onDraw(Canvas canvas) {
final int height = getHeight();
final int childCount = getChildCount();
final int dividerHeightPx =0;
final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null
? mCustomTabColorizer
: mDefaultTabColorizer;
canvas.drawColor(getResources().getColor(android.R.color.holo_orange_dark));
// Thick colored underline below the current selection
if (childCount > 0) {
View selectedTitle = getChildAt(mSelectedPosition);
int left = selectedTitle.getLeft();
int right = selectedTitle.getRight();
int color = tabColorizer.getIndicatorColor(mSelectedPosition);
if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);
if (color != nextColor) {
color = blendColors(nextColor, color, mSelectionOffset);
}
// Draw the selection partway between the tabs
View nextTitle = getChildAt(mSelectedPosition + 1);
left = (int) (mSelectionOffset * nextTitle.getLeft() +
(1.0f - mSelectionOffset) * left);
right = (int) (mSelectionOffset * nextTitle.getRight() +
(1.0f - mSelectionOffset) * right);
}
mSelectedIndicatorPaint.setColor(color);
canvas.drawRect(left, height - mSelectedIndicatorThickness, right,
height, mSelectedIndicatorPaint);
}
// Thin underline along the entire bottom edge
canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
// Vertical separators between the titles
int separatorTop = (height - dividerHeightPx) / 2;
for (int i = 0; i < childCount - 1; i++) {
View child = getChildAt(i);
mDividerPaint.setColor(tabColorizer.getDividerColor(i));
canvas.drawLine(child.getRight(), separatorTop, child.getRight(),
separatorTop + dividerHeightPx, mDividerPaint);
}
}
Related
Is there a way to have a checkbox with a specific colour for the border when it's unchecked and then when it's checked to have a different colour for the border and a black tick without using images?
Thanks!
Try this...used the Materialcheckbox
MaterialCheckBox.java
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
public class MaterialCheckBox extends View {
private Paint paintBlue;
private Paint paintWithe;
private Paint paintCenter;
private int borderColor = Color.GRAY;
private int backgroundColor = Color.BLUE;
private int doneShapeColor = Color.WHITE;
private int baseWidth;
private int borderWidth;
private int width, height;
private float[] points = new float[8];
private int DURATION = 200;
private boolean checked;
private float correctProgress;
private boolean drawRecting;
private boolean isAnim;
private OnCheckedChangeListener listener;
private float padding;
public MaterialCheckBox(Context context) {
this(context, null);
}
public MaterialCheckBox(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MaterialCheckBox(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public MaterialCheckBox(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
/**
* init
*
* #param context
*/
private void init(Context context) {
backgroundColor = getResources().getColor(R.color.dark_gray_app);
borderColor = getResources().getColor(R.color.dark_gray_app);
borderWidth = baseWidth = dp2px(2);
paintBlue = new Paint(Paint.ANTI_ALIAS_FLAG);
paintBlue.setColor(borderColor);
paintBlue.setStrokeWidth(borderWidth);
paintWithe = new Paint(Paint.ANTI_ALIAS_FLAG);
paintWithe.setColor(doneShapeColor);
paintWithe.setStrokeWidth(dp2px(2));
paintCenter = new Paint(Paint.ANTI_ALIAS_FLAG);
paintCenter.setColor(getResources().getColor(R.color.white));
paintCenter.setStrokeWidth(borderWidth);
setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
setChecked(!isChecked());
}
});
drawRecting = true;
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
height = width = Math.max(w, h);
points[0] = 101 / 378f * width;
points[1] = 0.5f * width;
points[2] = 163 / 378f * width;
points[3] = 251 / 378f * width;
points[4] = 149 / 378f * width;
points[5] = 250 / 378f * width;
points[6] = 278 / 378f * width;
points[7] = 122 / 378f * width;
padding = 57 / 378f * width;
}
/**
* draw checkbox
*
* #param canvas
*/
#Override
protected void onDraw(Canvas canvas) {
RectF rect = new RectF(padding, padding, width - padding, height - padding);
canvas.drawRoundRect(rect, baseWidth, baseWidth, paintBlue);
if (drawRecting) {
canvas.drawRect(padding + borderWidth, padding + borderWidth, width - padding - borderWidth, height - padding - borderWidth, paintCenter);
} else {
//画对号
if (correctProgress > 0) {
if (correctProgress < 1 / 3f) {
float x = points[0] + (points[2] - points[0]) * correctProgress;
float y = points[1] + (points[3] - points[1]) * correctProgress;
canvas.drawLine(points[0], points[1], x, y, paintWithe);
} else {
float x = points[4] + (points[6] - points[4]) * correctProgress;
float y = points[5] + (points[7] - points[5]) * correctProgress;
canvas.drawLine(points[0], points[1], points[2], points[3], paintWithe);
canvas.drawLine(points[4], points[5], x, y, paintWithe);
}
}
}
}
public void setBackgroundColor(int backgroundColor) {
this.backgroundColor = backgroundColor;
}
public void setDoneShapeColor(int doneShapeColor) {
this.doneShapeColor = doneShapeColor;
paintWithe.setColor(doneShapeColor);
}
public void setBorderColor(int borderColor) {
this.borderColor = borderColor;
}
public void setBorderWidth(int baseWidth) {
this.baseWidth = baseWidth;
}
public boolean isChecked() {
return checked;
}
public void setChecked(boolean checked) {
this.checked = checked;
if (checked) {
showRect();
} else {
hideCorrect();
}
}
private void hideRect() {
if (isAnim) {
return;
}
isAnim = true;
drawRecting = true;
ValueAnimator va = ValueAnimator.ofFloat(0, 1).setDuration(DURATION);
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
float p = (float) animation.getAnimatedValue();
float c = 1f - p;
borderWidth = (int) (baseWidth + c * (width - baseWidth));
paintBlue.setColor(evaluate(c, borderColor, backgroundColor));
invalidate();
if (p >= 1) {
isAnim = false;
if (listener != null) {
checked = false;
listener.onCheckedChanged(MaterialCheckBox.this, checked);
}
}
}
});
va.start();
}
private void showRect() {
if (isAnim) {
return;
}
isAnim = true;
drawRecting = true;
ValueAnimator va = ValueAnimator.ofFloat(0, 1).setDuration(DURATION);
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
float p = (float) animation.getAnimatedValue();
borderWidth = (int) (10 + p * (width - 10));
paintBlue.setColor(evaluate(p, borderColor, backgroundColor));
invalidate();
if (p >= 1) {
isAnim = false;
drawRecting = false;
showCorrect();
}
}
});
va.start();
}
private void showCorrect() {
if (isAnim) {
return;
}
isAnim = true;
correctProgress = 0;
drawRecting = false;
ValueAnimator va = ValueAnimator.ofFloat(0, 1).setDuration(DURATION);
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
correctProgress = (float) animation.getAnimatedValue();
invalidate();
if (correctProgress >= 1) {
isAnim = false;
if (listener != null) {
checked = true;
listener.onCheckedChanged(MaterialCheckBox.this, checked);
}
}
}
});
va.start();
}
private void hideCorrect() {
if (isAnim) {
return;
}
isAnim = true;
correctProgress = 1;
drawRecting = false;
ValueAnimator va = ValueAnimator.ofFloat(0, 1).setDuration(DURATION);
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
float p = (float) animation.getAnimatedValue();
correctProgress = 1f - p;
invalidate();
if (p >= 1) {
isAnim = false;
hideRect();
}
}
});
va.start();
}
public void setOnCheckedChangedListener(OnCheckedChangeListener listener) {
this.listener = listener;
}
interface OnCheckedChangeListener {
void onCheckedChanged(View view, boolean isChecked);
}
private int evaluate(float fraction, int startValue, int endValue) {
int startInt = startValue;
int startA = (startInt >> 24) & 0xff;
int startR = (startInt >> 16) & 0xff;
int startG = (startInt >> 8) & 0xff;
int startB = startInt & 0xff;
int endInt = endValue;
int endA = (endInt >> 24) & 0xff;
int endR = (endInt >> 16) & 0xff;
int endG = (endInt >> 8) & 0xff;
int endB = endInt & 0xff;
return ((startA + (int) (fraction * (endA - startA))) << 24)
| ((startR + (int) (fraction * (endR - startR))) << 16)
| ((startG + (int) (fraction * (endG - startG))) << 8)
| ((startB + (int) (fraction * (endB - startB))));
}
public int dp2px(float value) {
final float scale = getContext().getResources().getDisplayMetrics().densityDpi;
return (int) (value * (scale / 160) + 0.5f);
}
}
Xml
<.MaterialCheckBox
android:id="#+id/chk_remember"
android:layout_width="45dp"
android:layout_height="45dp"
/>
Code
MaterialCheckBox checkbox=(MaterialCheckBox)findViewById(R.id.checkbox);
checkbox.setOnCheckedChangedListener(new MaterialCheckBox.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(View view, boolean isChecked) {
if(isChecked)
{
}
else
{
}
}
});
or try this https://github.com/navasmdc/MaterialDesignLibrary#checkbox
Im using a ViewPager to show Images. The ViewPager is working correctly but there is an error with the indicator. When i swipe to the very last entry the indicator is only at round about 70% of the way. See the screen:
That is the problem. I dont know if the indicator is just too small, moving to slow or there are another hidden entries in the viewpager but since i cant swipe any further to the right i guess there is a bug with the indicator size or moving or something like that.
This is how i initializ my viewpager:
this.viewPager.setId(R.id.image_inspiration_pager);
this.viewPager.setClipToPadding(false);
this.viewPager.setPadding(0, 0, ((int) HelperPixel.convertDpToPixel(this.getContext(), 48)), 0);
this.viewPager.setAdapter(adapter);
this.viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
private static final float thresholdOffset = 0.5f;
private boolean scrollStarted = false;
private boolean checkDirection = false;
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (position == 0) {
viewPager.setPadding(0, 0, ((int) HelperPixel.convertDpToPixel(getContext(), 48)), 0);
return;
} else if (position == (adapter.getCount() - 1)) {
viewPager.setPadding(((int) HelperPixel.convertDpToPixel(getContext(), 48)), 0, 0, 0);
return;
}
if (this.checkDirection) {
if (thresholdOffset > positionOffset) {
viewPager.setPadding(0, 0, ((int) HelperPixel.convertDpToPixel(getContext(), 48)), 0);
} else {
viewPager.setPadding(((int) HelperPixel.convertDpToPixel(getContext(), 48)), 0, 0, 0);
}
this.checkDirection = false;
}
}
#Override
public void onPageSelected(int position) {}
#Override
public void onPageScrollStateChanged(int state) {
if (!this.scrollStarted && state == ViewPager.SCROLL_STATE_DRAGGING) {
this.scrollStarted = true;
this.checkDirection = true;
} else {
this.scrollStarted = false;
}
}
});
Implementation of TabLayout:
public class TabsSlidingLayout extends HorizontalScrollView {
private static final int TITLE_OFFSET_DIPS = 24;
private static final int TAB_VIEW_PADDING_DIPS = 16;
private static final int TAB_VIEW_TEXT_SIZE_SP = 14;
private int titleOffset;
private int tabViewLayoutId;
private int tabViewTextViewId;
private ViewPager viewPager;
private ViewPager.OnPageChangeListener pageChangeListener;
private final TabsSlidingStrip tabStrip;
public TabsSlidingLayout(Context context) {
this(context, null);
}
public TabsSlidingLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TabsSlidingLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.setHorizontalScrollBarEnabled(false);
this.setFillViewport(true);
this.titleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);
this.tabStrip = new TabsSlidingStrip(context);
this.addView(this.tabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
}
public void setCustomBackgroundColor(int color) {
this.tabStrip.setBackgroundColor(color);
}
public void setCustomTabColorizer(TabColorizer tabColorizer) {
this.tabStrip.setCustomTabColorizer(tabColorizer);
}
public void setSelectedIndicatorColors(int... colors) {
this.tabStrip.setSelectedIndicatorColors(colors);
}
public void setDividerColors(int... colors) {
this.tabStrip.setDividerColors(colors);
}
public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
this.pageChangeListener = listener;
}
public void setCustomTabView(int layoutResId, int textViewId) {
this.tabViewLayoutId = layoutResId;
this.tabViewTextViewId = textViewId;
}
public void setViewPager(ViewPager viewPager) {
this.tabStrip.removeAllViews();
this.viewPager = viewPager;
if (this.viewPager != null) {
this.viewPager.addOnPageChangeListener(new InternalViewPagerListener());
this.populateTabStrip();
}
}
protected TextView createDefaultTabView(Context context) {
TextView textView = new TextView(context);
textView.setGravity(Gravity.CENTER);
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);
textView.setTypeface(Typeface.DEFAULT_BOLD);
textView.setTextColor(ContextCompat.getColor(context, R.color.white));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
TypedValue outValue = new TypedValue();
this.getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground, outValue, true);
textView.setBackgroundResource(outValue.resourceId);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
textView.setAllCaps(true);
}
int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density);
textView.setPadding(padding, padding, padding, padding);
return textView;
}
private void populateTabStrip() {
final PagerAdapter adapter = this.viewPager.getAdapter();
final View.OnClickListener tabClickListener = new TabClickListener();
for (int i = 0; i < adapter.getCount(); i++) {
View tabView = null;
TextView tabTitleView = null;
if (this.tabViewLayoutId != 0) {
tabView = LayoutInflater.from(this.getContext()).inflate(this.tabViewLayoutId, this.tabStrip, false);
tabTitleView = (TextView) tabView.findViewById(this.tabViewTextViewId);
}
if (tabView == null) {
tabView = createDefaultTabView(this.getContext());
}
if (tabTitleView == null && TextView.class.isInstance(tabView)) {
tabTitleView = (TextView) tabView;
}
tabTitleView.setText(adapter.getPageTitle(i));
tabView.setOnClickListener(tabClickListener);
this.tabStrip.addView(tabView);
}
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (this.viewPager != null) {
this.scrollToTab(this.viewPager.getCurrentItem(), 0);
}
}
private void scrollToTab(int tabIndex, int positionOffset) {
final int tabStripChildCount = this.tabStrip.getChildCount();
if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
return;
}
View selectedChild = this.tabStrip.getChildAt(tabIndex);
if (selectedChild != null) {
int targetScrollX = selectedChild.getLeft() + positionOffset;
if (tabIndex > 0 || positionOffset > 0) {
targetScrollX -= this.titleOffset;
}
this.scrollTo(targetScrollX, 0);
}
}
public interface TabColorizer {
int getIndicatorColor(int position);
int getDividerColor(int position);
}
private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
private int scrollState = -1;
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
int tabStripChildCount = tabStrip.getChildCount();
if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
return;
}
tabStrip.onViewPagerPageChanged(position, positionOffset);
View selectedTitle = tabStrip.getChildAt(position);
int extraOffset = (selectedTitle != null) ? (int) (positionOffset * selectedTitle.getWidth()) : 0;
scrollToTab(position, extraOffset);
if (pageChangeListener != null) {
pageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
}
#Override
public void onPageScrollStateChanged(int state) {
this.scrollState = state;
if (pageChangeListener != null) {
pageChangeListener.onPageScrollStateChanged(state);
}
}
#Override
public void onPageSelected(int position) {
if (this.scrollState == ViewPager.SCROLL_STATE_IDLE) {
tabStrip.onViewPagerPageChanged(position, 0f);
scrollToTab(position, 0);
}
if (pageChangeListener != null) {
pageChangeListener.onPageSelected(position);
}
}
}
private class TabClickListener implements View.OnClickListener {
#Override
public void onClick(View view) {
for (int i = 0; i < tabStrip.getChildCount(); i++) {
if (view == tabStrip.getChildAt(i)) {
viewPager.setCurrentItem(i);
return;
}
}
}
}
}
TabSlidingStrip implementation:
class TabsSlidingStrip extends LinearLayout {
private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 0;
private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 2;
private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFFFFFFFF;
private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 0;
private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20;
private static final float DEFAULT_DIVIDER_HEIGHT = 0.0f;
private final int bottomBorderThickness;
private final Paint bottomBorderPaint;
private final int selectedIndicatorThickness;
private final Paint selectedIndicatorPaint;
private final Paint dividerPaint;
private final float dividerHeight;
private int selectedPosition;
private float selectionOffset;
private TabsSlidingLayout.TabColorizer customTabColorizer;
private final SimpleTabColorizer defaultTabColorizer;
public TabsSlidingStrip(Context context) {
this(context, null);
}
public TabsSlidingStrip(Context context, AttributeSet attrs) {
super(context, attrs);
this.setWillNotDraw(false);
final float density = getResources().getDisplayMetrics().density;
TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(android.R.attr.colorForeground, outValue, true);
final int themeForegroundColor = outValue.data;
this.setBackgroundColor(ContextCompat.getColor(context, R.color.primaryColor));
this.defaultTabColorizer = new SimpleTabColorizer();
this.defaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);
this.defaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor, DEFAULT_DIVIDER_COLOR_ALPHA));
this.bottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
this.bottomBorderPaint = new Paint();
this.bottomBorderPaint.setColor(setColorAlpha(themeForegroundColor, DEFAULT_BOTTOM_BORDER_COLOR_ALPHA));
this.selectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
this.selectedIndicatorPaint = new Paint();
this.dividerHeight = DEFAULT_DIVIDER_HEIGHT;
this.dividerPaint = new Paint();
this.dividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density));
}
void setCustomTabColorizer(TabsSlidingLayout.TabColorizer customTabColorizer) {
this.customTabColorizer = customTabColorizer;
this.invalidate();
}
void setSelectedIndicatorColors(int... colors) {
this.customTabColorizer = null;
this.defaultTabColorizer.setIndicatorColors(colors);
this.invalidate();
}
void setDividerColors(int... colors) {
this.customTabColorizer = null;
this.defaultTabColorizer.setDividerColors(colors);
this.invalidate();
}
void onViewPagerPageChanged(int position, float positionOffset) {
this.selectedPosition = position;
this.selectionOffset = positionOffset;
this.invalidate();
}
#Override
protected void onDraw(Canvas canvas) {
final int height = this.getHeight();
final int childCount = this.getChildCount();
final int dividerHeightPx = (int) (Math.min(Math.max(0f, this.dividerHeight), 1f) * height);
final TabsSlidingLayout.TabColorizer tabColorizer = this.customTabColorizer != null ? this.customTabColorizer : this.defaultTabColorizer;
if (childCount > 0) {
View selectedTitle = getChildAt(this.selectedPosition);
int left = selectedTitle.getLeft();
int right = selectedTitle.getRight();
int color = tabColorizer.getIndicatorColor(this.selectedPosition);
if (this.selectionOffset > 0f && this.selectedPosition < (getChildCount() - 1)) {
int nextColor = tabColorizer.getIndicatorColor(this.selectedPosition + 1);
if (color != nextColor) {
color = blendColors(nextColor, color, this.selectionOffset);
}
View nextTitle = getChildAt(this.selectedPosition + 1);
left = (int) (this.selectionOffset * nextTitle.getLeft() + (1.0f - this.selectionOffset) * left);
right = (int) (this.selectionOffset * nextTitle.getRight() + (1.0f - this.selectionOffset) * right);
}
this.selectedIndicatorPaint.setColor(color);
canvas.drawRect(left, height - this.selectedIndicatorThickness, right, height, this.selectedIndicatorPaint);
}
canvas.drawRect(0, height - this.bottomBorderThickness, this.getWidth(), height, this.bottomBorderPaint);
int separatorTop = (height - dividerHeightPx) / 2;
for (int i = 0; i < childCount - 1; i++) {
View child = getChildAt(i);
this.dividerPaint.setColor(tabColorizer.getDividerColor(i));
canvas.drawLine(child.getRight(), separatorTop, child.getRight(), separatorTop + dividerHeightPx, this.dividerPaint);
}
}
private static int setColorAlpha(int color, byte alpha) {
return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
}
private static int blendColors(int color1, int color2, float ratio) {
final float inverseRation = 1f - ratio;
float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
return Color.rgb((int) r, (int) g, (int) b);
}
private static class SimpleTabColorizer implements TabsSlidingLayout.TabColorizer {
private int[] indicatorColors;
private int[] dividerColors;
#Override
public final int getIndicatorColor(int position) {
return this.indicatorColors[position % this.indicatorColors.length];
}
void setIndicatorColors(int... colors) {
this.indicatorColors = colors;
}
#Override
public final int getDividerColor(int position) {
return this.dividerColors[position % this.dividerColors.length];
}
void setDividerColors(int... colors) {
this.dividerColors = colors;
}
}
}
The first screenshot is from a tablet. Here is a screen from my smartphone:
As you can see for the smartphone the gap is much smaller than on the tablet.
Any idea what could be the problem?
Add the following code to the bottom of createDefaultTabView:
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = windowManager.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
textView.setWidth(size.x / this.viewPager.getAdapter().getCount());
i`m trying to use the AutoResizeTextView class like discussed here:
enter link description here
But if i do
textview.setMaxLines(1);
the text doesnt stay vertically centered. Its only horizontally centered. If i do:
textview.setMaxLines(2);
it works, but i need only one line.
i`m using exactly this class:
package com.example.myapp;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.RectF;
import android.os.Build;
import android.text.Layout.Alignment;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.SparseIntArray;
import android.util.TypedValue;
import android.widget.TextView;
public class AutoResizeTextview extends TextView {
private interface SizeTester {
/**
*
* #param suggestedSize
* Size of text to be tested
* #param availableSpace
* available space in which text must fit
* #return an integer < 0 if after applying {#code suggestedSize} to
* text, it takes less space than {#code availableSpace}, > 0
* otherwise
*/
public int onTestSize(int suggestedSize, RectF availableSpace);
}
private RectF mTextRect = new RectF();
private RectF mAvailableSpaceRect;
private SparseIntArray mTextCachedSizes;
private TextPaint mPaint;
private float mMaxTextSize;
private float mSpacingMult = 1.0f;
private float mSpacingAdd = 0.0f;
private float mMinTextSize = 20;
private int mWidthLimit;
private static final int NO_LINE_LIMIT = -1;
private int mMaxLines;
private boolean mEnableSizeCache = true;
private boolean mInitiallized;
public AutoResizeTextview(Context context) {
super(context);
initialize();
}
public AutoResizeTextview(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
public AutoResizeTextview(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initialize();
}
private void initialize() {
mPaint = new TextPaint(getPaint());
mMaxTextSize = getTextSize();
mAvailableSpaceRect = new RectF();
mTextCachedSizes = new SparseIntArray();
if (mMaxLines == 0) {
// no value was assigned during construction
mMaxLines = NO_LINE_LIMIT;
}
mInitiallized = true;
}
#Override
public void setText(final CharSequence text, BufferType type) {
super.setText(text, type);
adjustTextSize(text.toString());
}
#Override
public void setTextSize(float size) {
mMaxTextSize = size;
mTextCachedSizes.clear();
adjustTextSize(getText().toString());
}
#Override
public void setMaxLines(int maxlines) {
super.setMaxLines(maxlines);
mMaxLines = maxlines;
reAdjust();
}
public int getMaxLines() {
return mMaxLines;
}
#Override
public void setSingleLine() {
super.setSingleLine();
mMaxLines = 1;
reAdjust();
}
#Override
public void setSingleLine(boolean singleLine) {
super.setSingleLine(singleLine);
if (singleLine) {
mMaxLines = 1;
} else {
mMaxLines = NO_LINE_LIMIT;
}
reAdjust();
}
#Override
public void setLines(int lines) {
super.setLines(lines);
mMaxLines = lines;
reAdjust();
}
#Override
public void setTextSize(int unit, float size) {
Context c = getContext();
Resources r;
if (c == null)
r = Resources.getSystem();
else
r = c.getResources();
mMaxTextSize = TypedValue.applyDimension(unit, size,
r.getDisplayMetrics());
mTextCachedSizes.clear();
adjustTextSize(getText().toString());
}
#Override
public void setLineSpacing(float add, float mult) {
super.setLineSpacing(add, mult);
mSpacingMult = mult;
mSpacingAdd = add;
}
/**
* Set the lower text size limit and invalidate the view
*
* #param minTextSize
*/
public void setMinTextSize(float minTextSize) {
mMinTextSize = minTextSize;
reAdjust();
}
private void reAdjust() {
adjustTextSize(getText().toString());
}
private void adjustTextSize(String string) {
if (!mInitiallized) {
return;
}
int startSize = (int) mMinTextSize;
int heightLimit = getMeasuredHeight() - getCompoundPaddingBottom()
- getCompoundPaddingTop();
mWidthLimit = getMeasuredWidth() - getCompoundPaddingLeft()
- getCompoundPaddingRight();
mAvailableSpaceRect.right = mWidthLimit;
mAvailableSpaceRect.bottom = heightLimit;
super.setTextSize(
TypedValue.COMPLEX_UNIT_PX,
efficientTextSizeSearch(startSize, (int) mMaxTextSize,
mSizeTester, mAvailableSpaceRect));
}
private final SizeTester mSizeTester = new SizeTester() {
#TargetApi(Build.VERSION_CODES.JELLY_BEAN)
#Override
public int onTestSize(int suggestedSize, RectF availableSPace) {
mPaint.setTextSize(suggestedSize);
String text = getText().toString();
boolean singleline = getMaxLines() == 1;
if (singleline) {
mTextRect.bottom = mPaint.getFontSpacing();
mTextRect.right = mPaint.measureText(text);
} else {
StaticLayout layout = new StaticLayout(text, mPaint,
mWidthLimit, Alignment.ALIGN_NORMAL, mSpacingMult,
mSpacingAdd, true);
// return early if we have more lines
if (getMaxLines() != NO_LINE_LIMIT
&& layout.getLineCount() > getMaxLines()) {
return 1;
}
mTextRect.bottom = layout.getHeight();
int maxWidth = -1;
for (int i = 0; i < layout.getLineCount(); i++) {
if (maxWidth < layout.getLineWidth(i)) {
maxWidth = (int) layout.getLineWidth(i);
}
}
mTextRect.right = maxWidth;
}
mTextRect.offsetTo(0, 0);
if (availableSPace.contains(mTextRect)) {
// may be too small, don't worry we will find the best match
return -1;
} else {
// too big
return 1;
}
}
};
/**
* Enables or disables size caching, enabling it will improve performance
* where you are animating a value inside TextView. This stores the font
* size against getText().length() Be careful though while enabling it as 0
* takes more space than 1 on some fonts and so on.
*
* #param enable
* enable font size caching
*/
public void enableSizeCache(boolean enable) {
mEnableSizeCache = enable;
mTextCachedSizes.clear();
adjustTextSize(getText().toString());
}
private int efficientTextSizeSearch(int start, int end,
SizeTester sizeTester, RectF availableSpace) {
if (!mEnableSizeCache) {
return binarySearch(start, end, sizeTester, availableSpace);
}
String text = getText().toString();
int key = text == null ? 0 : text.length();
int size = mTextCachedSizes.get(key);
if (size != 0) {
return size;
}
size = binarySearch(start, end, sizeTester, availableSpace);
mTextCachedSizes.put(key, size);
return size;
}
private static int binarySearch(int start, int end, SizeTester sizeTester,
RectF availableSpace) {
int lastBest = start;
int lo = start;
int hi = end - 1;
int mid = 0;
while (lo <= hi) {
mid = (lo + hi) >>> 1;
int midValCmp = sizeTester.onTestSize(mid, availableSpace);
if (midValCmp < 0) {
lastBest = lo;
lo = mid + 1;
} else if (midValCmp > 0) {
hi = mid - 1;
lastBest = hi;
} else {
return mid;
}
}
// make sure to return last best
// this is what should always be returned
return lastBest;
}
#Override
protected void onTextChanged(final CharSequence text, final int start,
final int before, final int after) {
super.onTextChanged(text, start, before, after);
reAdjust();
}
#Override
protected void onSizeChanged(int width, int height, int oldwidth,
int oldheight) {
mTextCachedSizes.clear();
super.onSizeChanged(width, height, oldwidth, oldheight);
if (width != oldwidth || height != oldheight) {
reAdjust();
}
}
}
UPDATE
changing the anonymous class "private final SizeTester mSizeTester = new SizeTester()" like:
private final SizeTester mSizeTester = new SizeTester()
{
#TargetApi(Build.VERSION_CODES.JELLY_BEAN)
#Override
public int onTestSize(int suggestedSize, RectF availableSPace)
{
mPaint.setTextSize(suggestedSize);
String text = getText().toString();
boolean singleline = getMaxLines() == 1;
// if (singleline) { //my comment-out
// mTextRect.bottom = mPaint.getFontSpacing(); //my comment-out
// mTextRect.right = mPaint.measureText(text); //my comment-out
// } else { //my comment-out
StaticLayout layout = new StaticLayout(text, mPaint, mWidthLimit, Alignment.ALIGN_NORMAL, mSpacingMult,
mSpacingAdd, true);
// return early if we have more lines
if (getMaxLines() != NO_LINE_LIMIT && layout.getLineCount() > getMaxLines())
{
return 1;
}
mTextRect.bottom = layout.getHeight();
int maxWidth = -1;
for (int i = 0; i < layout.getLineCount(); i++)
{
if (maxWidth < layout.getLineWidth(i))
{
maxWidth = (int) layout.getLineWidth(i);
}
}
mTextRect.right = maxWidth;
// } //my comment-out
mTextRect.offsetTo(0, 0);
if (availableSPace.contains(mTextRect))
{
// may be too small, don't worry we will find the best match
return -1;
}
else
{
// too big
return 1;
}
}
};
....solves it. See the Lines with "my comment-out" at the end. Not sure if there are any negative side-effects though yet.
am looking to create a customs slider component with the functionality of s seek bar.
I have been following this tutorial
http://www.androidenea.com/2011/10/creating-custom-view-part-1-graphics.html
however, I wish to use my own graphics which aren't .9.png graphics. I also need to resize the seek bar and the slider.
If anyone can help on this I'd greatly appreciate it. I have it to a stage where I have it resized and the graphics are changed however, when I use my finger to slide upwards I need to slide to the top of the screen in order for my seebar to reach the top of the frame.
Please any help is appreciated, this is what I've implemented so far:
package com.enea.training.customview;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class CustomSlider extends View {
interface CustomSliderPositionListener {
void onPositionChange(float newPosition);
}
private final Drawable mIndicator;
private final Drawable mBackground;
private float mMin;
private float mMax;
private float mPosition;
private Rect mViewRect;
private int mIndicatorOffset;
private int mIndicatorMaxPos;
private int mIndicatorMinPos;
private CustomSliderPositionListener mPositionListener;
private boolean mIsVertical;
public CustomSlider(final Context context) {
this(context, null, 0);
}
public CustomSlider(final Context context, final AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomSlider(final Context context, final AttributeSet attrs,
final int defStyle) {
super(context, attrs, defStyle);
final TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.CustomSlider);
final int vertical = a.getInt(R.styleable.CustomSlider_orientation, 0);
mIsVertical = (vertical != 0);
final float max = a.getFloat(R.styleable.CustomSlider_max, 1.0f);
final float min = a.getFloat(R.styleable.CustomSlider_min, -1.0f);
setMinMax(min, max);
final Resources res = context.getResources();
if (mIsVertical) {
mIndicator = res.getDrawable(R.drawable.beer_with_arrows);
mBackground = res.getDrawable(R.drawable.glass);
} else {
mIndicator = res.getDrawable(R.drawable.indicator_horizontal);
mBackground = res.getDrawable(R.drawable.background_horizontal);
}
mPosition =(mMax - 1) ;/// 2 + mMin;
mPositionListener = null;
setOnTouchListener(new OnTouchListener() {
public boolean onTouch(final View v, final MotionEvent event) {
final float pos;
if (mIsVertical) {
float divisor = mIndicatorMinPos-mIndicatorMaxPos;
pos = (mMax - ((mMax - mMin) / divisor)
* event.getY());
} else {
pos = (mMin + ((mMax - mMin) / (mIndicatorMaxPos - mIndicatorMinPos))
* event.getX());
}
setPosition(pos);
return true;
}
});
}
#Override
protected void onDraw(final Canvas canvas) {
if (mViewRect == null) {
mViewRect = new Rect();
mViewRect.set(20,20,20,20);
getDrawingRect(mViewRect);
if (mIsVertical) {
mIndicatorOffset = mIndicator.getIntrinsicHeight();
mIndicatorMaxPos = mViewRect.top + mIndicatorOffset;
mIndicatorMinPos = mViewRect.bottom - mIndicatorOffset;
} else {
mIndicatorOffset = mIndicator.getIntrinsicWidth();
mIndicatorMaxPos = mViewRect.right - mIndicatorOffset;
mIndicatorMinPos = mViewRect.left + mIndicatorOffset;
}
mBackground.setBounds(mViewRect.left, mViewRect.top, mViewRect.right,
mViewRect.bottom);
}
final float pos;
final int left;
final int right;
final int top;
final int bottom;
if (mIsVertical) {
pos = mIndicatorMaxPos
+ ((mIndicatorMinPos - mIndicatorMaxPos) / (mMax - mMin))
* (mMax - mPosition);
left = mViewRect.centerX() - (mIndicator.getIntrinsicWidth() / 2);
top = (int) pos - (mBackground.getIntrinsicHeight() / 2);
} else {
pos = mIndicatorMinPos
+ ((mIndicatorMaxPos - mIndicatorMinPos) / (mMax - mMin))
* (mPosition - mMin);
left = (int) pos - (mIndicator.getIntrinsicWidth() / 2);
top = mViewRect.centerY() - (mIndicator.getIntrinsicHeight() / 2);
}
right = left + mIndicator.getIntrinsicWidth();
bottom = top + mIndicator.getIntrinsicHeight();
mIndicator.setBounds(left, top, right, bottom);
mIndicator.draw(canvas);
mBackground.draw(canvas);
}
#Override
protected void onMeasure(final int widthMeasureSpec,
final int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mIsVertical) {
int desiredWidth = 100;
int desiredHeight = 100;
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
//Measure Width
if (widthMode == MeasureSpec.EXACTLY) {
//Must be this size
width = widthSize;
} else if (widthMode == MeasureSpec.AT_MOST) {
//Can't be bigger than...
width = Math.min(desiredWidth, widthSize);
} else {
//Be whatever you want
width = desiredWidth;
}
//Measure Height
if (heightMode == MeasureSpec.EXACTLY) {
//Must be this size
height = heightSize;
} else if (heightMode == MeasureSpec.AT_MOST) {
//Can't be bigger than...
height = Math.min(desiredHeight, heightSize);
} else {
//Be whatever you want
height = desiredHeight;
}
//MUST CALL THIS
setMeasuredDimension(width, height);
//setMeasuredDimension(getSuggestedMinimumWidth(),getSuggestedMinimumHeight());
// setMeasuredDimension(mIndicator.getIntrinsicWidth(), getMeasuredHeight());
} else {
setMeasuredDimension(getMeasuredWidth(), mBackground.getIntrinsicHeight());
}
}
public float getMin() {
return mMin;
}
public float getMax() {
return mMax;
}
public float getPosition() {
return mPosition;
}
public void setPosition(float position) {
position = within(position, mMin, mMax);
if (position != mPosition) {
mPosition = position;
invalidate();
if (mPositionListener != null) {
mPositionListener.onPositionChange(mPosition);
}
}
}
private static float within(float position, final float min, final float max) {
if (position < min) {
position = min;
}
if (position > max) {
position = max;
}
return position;
}
public void setMinMax(final float min, final float max) {
if ((min != mMin) || (max != mMax)) {
if (min > max) {
throw new IllegalArgumentException(
"setMinMax: min must be smaller than max.");
}
mMin = min;
mMax = max;
setPosition(mPosition);
invalidate();
}
}
public void setPositionListener(final CustomSliderPositionListener listener) {
mPositionListener = listener;
}
}
****EDIT****
So below I have done up a rough pic to show what I'm trying to do. So my custom view includes the slider, and frame (background and indicator). The slider (indicator) is dragged by user up and down inside the red background..
I would apprecaite any help and thank you for your help so far...
I
I am using a custom layout to display a varied number of buttons and intercept their clicks. Here is the source code for the custom layout:
import android.content.Context;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Adapter;
import android.widget.AdapterView;
public class FlowLayout extends AdapterView<Adapter> {
public static final int HORIZONTAL = 0;
public static final int VERTICAL = 1;
private static final int INVALID_INDEX = -1;
private static final int TOUCH_STATE_RESTING = 0;
private static final int TOUCH_STATE_CLICK = 1;
private int mTouchState = TOUCH_STATE_RESTING;
private Rect mRect;
private Runnable mLongPressRunnable;
private int mTouchStartX;
private int mTouchStartY;
private int horizontalSpacing = 0;
private int verticalSpacing = 0;
private int orientation = 0;
private boolean debugDraw = false;
private Adapter mAdapter;
private final AdapterObserver mObserver = new AdapterObserver();
public FlowLayout(Context context) {
super(context);
this.readStyleParameters(context, null);
}
public FlowLayout(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
this.readStyleParameters(context, attributeSet);
}
public FlowLayout(Context context, AttributeSet attributeSet, int defStyle) {
super(context, attributeSet, defStyle);
this.readStyleParameters(context, attributeSet);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec) - this.getPaddingRight() - this.getPaddingLeft();
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec) - this.getPaddingRight() - this.getPaddingLeft();
int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
int size;
int mode;
if (orientation == HORIZONTAL) {
size = sizeWidth;
mode = modeWidth;
} else {
size = sizeHeight;
mode = modeHeight;
}
int lineThicknessWithSpacing = 0;
int lineThickness = 0;
int lineLengthWithSpacing = 0;
int lineLength;
int prevLinePosition = 0;
int controlMaxLength = 0;
int controlMaxThickness = 0;
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
child.measure(
MeasureSpec.makeMeasureSpec(sizeWidth, modeWidth == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST : modeWidth),
MeasureSpec.makeMeasureSpec(sizeHeight, modeHeight == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST : modeHeight)
);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
int hSpacing = this.getHorizontalSpacing(lp);
int vSpacing = this.getVerticalSpacing(lp);
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
int childLength;
int childThickness;
int spacingLength;
int spacingThickness;
if (orientation == HORIZONTAL) {
childLength = childWidth;
childThickness = childHeight;
spacingLength = hSpacing;
spacingThickness = vSpacing;
} else {
childLength = childHeight;
childThickness = childWidth;
spacingLength = vSpacing;
spacingThickness = hSpacing;
}
lineLength = lineLengthWithSpacing + childLength;
lineLengthWithSpacing = lineLength + spacingLength;
boolean newLine = lp.newLine || (mode != MeasureSpec.UNSPECIFIED && lineLength > size);
if (newLine) {
prevLinePosition = prevLinePosition + lineThicknessWithSpacing;
lineThickness = childThickness;
lineLength = childLength;
lineThicknessWithSpacing = childThickness + spacingThickness;
lineLengthWithSpacing = lineLength + spacingLength;
}
lineThicknessWithSpacing = Math.max(lineThicknessWithSpacing, childThickness + spacingThickness);
lineThickness = Math.max(lineThickness, childThickness);
int posX;
int posY;
if (orientation == HORIZONTAL) {
posX = getPaddingLeft() + lineLength - childLength;
posY = getPaddingTop() + prevLinePosition;
} else {
posX = getPaddingLeft() + prevLinePosition;
posY = getPaddingTop() + lineLength - childHeight;
}
lp.setPosition(posX, posY);
controlMaxLength = Math.max(controlMaxLength, lineLength);
controlMaxThickness = prevLinePosition + lineThickness;
}
if (orientation == HORIZONTAL) {
this.setMeasuredDimension(resolveSize(controlMaxLength, widthMeasureSpec), resolveSize(controlMaxThickness, heightMeasureSpec));
} else {
this.setMeasuredDimension(resolveSize(controlMaxThickness, widthMeasureSpec), resolveSize(controlMaxLength, heightMeasureSpec));
}
}
private int getVerticalSpacing(LayoutParams lp) {
int vSpacing;
if (lp.verticalSpacingSpecified()) {
vSpacing = lp.verticalSpacing;
} else {
vSpacing = this.verticalSpacing;
}
return vSpacing;
}
private int getHorizontalSpacing(LayoutParams lp) {
int hSpacing;
if (lp.horizontalSpacingSpecified()) {
hSpacing = lp.horizontalSpacing;
} else {
hSpacing = this.horizontalSpacing;
}
return hSpacing;
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
child.layout(lp.x, lp.y, lp.x + child.getMeasuredWidth(), lp.y + child.getMeasuredHeight());
}
}
#Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
boolean more = super.drawChild(canvas, child, drawingTime);
this.drawDebugInfo(canvas, child);
return more;
}
#Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}
#Override
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
#Override
public LayoutParams generateLayoutParams(AttributeSet attributeSet) {
return new LayoutParams(getContext(), attributeSet);
}
#Override
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(p);
}
private void readStyleParameters(Context context, AttributeSet attributeSet) {
TypedArray a = context.obtainStyledAttributes(attributeSet, R.styleable.FlowLayout);
try {
horizontalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_horizontalSpacing, 0);
verticalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_verticalSpacing, 0);
orientation = a.getInteger(R.styleable.FlowLayout_orientation, HORIZONTAL);
debugDraw = a.getBoolean(R.styleable.FlowLayout_debugDraw, false);
} finally {
a.recycle();
}
}
private void drawDebugInfo(Canvas canvas, View child) {
if (!debugDraw) {
return;
}
Paint childPaint = this.createPaint(0xffffff00);
Paint layoutPaint = this.createPaint(0xff00ff00);
Paint newLinePaint = this.createPaint(0xffff0000);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (lp.horizontalSpacing > 0) {
float x = child.getRight();
float y = child.getTop() + child.getHeight() / 2.0f;
canvas.drawLine(x, y, x + lp.horizontalSpacing, y, childPaint);
canvas.drawLine(x + lp.horizontalSpacing - 4.0f, y - 4.0f, x + lp.horizontalSpacing, y, childPaint);
canvas.drawLine(x + lp.horizontalSpacing - 4.0f, y + 4.0f, x + lp.horizontalSpacing, y, childPaint);
} else if (this.horizontalSpacing > 0) {
float x = child.getRight();
float y = child.getTop() + child.getHeight() / 2.0f;
canvas.drawLine(x, y, x + this.horizontalSpacing, y, layoutPaint);
canvas.drawLine(x + this.horizontalSpacing - 4.0f, y - 4.0f, x + this.horizontalSpacing, y, layoutPaint);
canvas.drawLine(x + this.horizontalSpacing - 4.0f, y + 4.0f, x + this.horizontalSpacing, y, layoutPaint);
}
if (lp.verticalSpacing > 0) {
float x = child.getLeft() + child.getWidth() / 2.0f;
float y = child.getBottom();
canvas.drawLine(x, y, x, y + lp.verticalSpacing, childPaint);
canvas.drawLine(x - 4.0f, y + lp.verticalSpacing - 4.0f, x, y + lp.verticalSpacing, childPaint);
canvas.drawLine(x + 4.0f, y + lp.verticalSpacing - 4.0f, x, y + lp.verticalSpacing, childPaint);
} else if (this.verticalSpacing > 0) {
float x = child.getLeft() + child.getWidth() / 2.0f;
float y = child.getBottom();
canvas.drawLine(x, y, x, y + this.verticalSpacing, layoutPaint);
canvas.drawLine(x - 4.0f, y + this.verticalSpacing - 4.0f, x, y + this.verticalSpacing, layoutPaint);
canvas.drawLine(x + 4.0f, y + this.verticalSpacing - 4.0f, x, y + this.verticalSpacing, layoutPaint);
}
if (lp.newLine) {
if (orientation == HORIZONTAL) {
float x = child.getLeft();
float y = child.getTop() + child.getHeight() / 2.0f;
canvas.drawLine(x, y - 6.0f, x, y + 6.0f, newLinePaint);
} else {
float x = child.getLeft() + child.getWidth() / 2.0f;
float y = child.getTop();
canvas.drawLine(x - 6.0f, y, x + 6.0f, y, newLinePaint);
}
}
}
private Paint createPaint(int color) {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(color);
paint.setStrokeWidth(2.0f);
return paint;
}
public static class LayoutParams extends ViewGroup.LayoutParams {
private static int NO_SPACING = -1;
private int x;
private int y;
private int horizontalSpacing = NO_SPACING;
private int verticalSpacing = NO_SPACING;
private boolean newLine = false;
public LayoutParams(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
this.readStyleParameters(context, attributeSet);
}
public LayoutParams(int width, int height) {
super(width, height);
}
public LayoutParams(ViewGroup.LayoutParams layoutParams) {
super(layoutParams);
}
public boolean horizontalSpacingSpecified() {
return horizontalSpacing != NO_SPACING;
}
public boolean verticalSpacingSpecified() {
return verticalSpacing != NO_SPACING;
}
public void setHorizontalSpacing(int hs) {
horizontalSpacing = hs;
}
public void setVerticalSpacing(int vs) {
verticalSpacing = vs;
}
public void setPosition(int x, int y) {
this.x = x;
this.y = y;
}
private void readStyleParameters(Context context, AttributeSet attributeSet) {
TypedArray a = context.obtainStyledAttributes(attributeSet, R.styleable.FlowLayout_LayoutParams);
try {
horizontalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_LayoutParams_layout_horizontalSpacing, NO_SPACING);
verticalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_LayoutParams_layout_verticalSpacing, NO_SPACING);
newLine = a.getBoolean(R.styleable.FlowLayout_LayoutParams_layout_newLine, false);
} finally {
a.recycle();
}
}
}
#Override
public Adapter getAdapter() {
return mAdapter;
}
#Override
public View getSelectedView() {
throw new UnsupportedOperationException("Not supported");
}
#Override
public void setAdapter(Adapter adapter) {
if (mAdapter != null) {
mAdapter.unregisterDataSetObserver(mObserver);
}
mAdapter = adapter;
mAdapter.registerDataSetObserver(mObserver);
refresh();
}
public void refresh() {
removeAllViewsInLayout();
for (int i = 0; i < mAdapter.getCount(); i++) {
final View view = mAdapter.getView(i, null, this);
ViewGroup.LayoutParams params = view.getLayoutParams();
if (params == null) {
params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
addViewInLayout(view, i, params, true);
}
postInvalidate();
requestLayout();
}
public class AdapterObserver extends DataSetObserver {
#Override
public void onChanged() {
refresh();
}
#Override
public void onInvalidated() {
}
}
#Override
public void setSelection(int position) {
throw new UnsupportedOperationException("Not supported");
}
// Touch detection
#Override
public boolean onTouchEvent(MotionEvent event) {
if (getChildCount() == 0) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startTouch(event);
break;
case MotionEvent.ACTION_UP:
if (mTouchState == TOUCH_STATE_CLICK) {
clickChildAt((int)event.getX(), (int)event.getY());
}
endTouch();
break;
default:
endTouch();
break;
}
return true;
}
/**
* Sets and initializes all things that need to when we start a touch
* gesture.
*
* #param event The down event
*/
private void startTouch(final MotionEvent event) {
mTouchStartX = (int)event.getX();
mTouchStartY = (int)event.getY();
startLongPressCheck();
mTouchState = TOUCH_STATE_CLICK;
}
private void startLongPressCheck() {
if (mLongPressRunnable == null) {
mLongPressRunnable = new Runnable() {
public void run() {
if (mTouchState == TOUCH_STATE_CLICK) {
final int index = getContainingChildIndex(mTouchStartX, mTouchStartY);
if (index != INVALID_INDEX) { longClickChild(index); mTouchState = TOUCH_STATE_RESTING; }
}
}
};
}
postDelayed(mLongPressRunnable, 300); //ViewConfiguration.getLongPressTimeout()
}
private void longClickChild(final int index) {
final View itemView = getChildAt(index);
final int position = index;
final long id = mAdapter.getItemId(position);
final OnItemLongClickListener listener = getOnItemLongClickListener();
if (listener != null) { listener.onItemLongClick(this, itemView, position, id); }
}
private int getContainingChildIndex(final int x, final int y) {
if (mRect == null) { mRect = new Rect(); }
for (int index = 0; index < getChildCount(); index++) {
getChildAt(index).getHitRect(mRect);
if (mRect.contains(x, y)) { return index; }
}
return INVALID_INDEX;
}
private void endTouch() {
removeCallbacks(mLongPressRunnable);
mTouchState = TOUCH_STATE_RESTING;
}
private void clickChildAt(final int x, final int y) {
final int index = getContainingChildIndex(x, y);
if (index != INVALID_INDEX) {
final View itemView = getChildAt(index);
final int position = index;
final long id = mAdapter.getItemId(position);
performItemClick(itemView, position, id);
}
}
}
This code works on my test device which is a Google Nexus S with Android 4.1.2, meaning that the buttons are clickable. Yet I got reports that the buttons are unresponsive on other devices such as Android Casio C771 with Android version 2.3.3 and Verizon LG VS840 with Android 4.0.4.
Can you please tell me what could cause this discrepancy and how can I fix it?
Thank you
Things to look out for:
It is essential that the Views created for the adapter are enabled for click and touch action. After they are created:
View.setEnabled(true);
When you create the AdapterView and the BaseApapter, remember to both setOnItemClickListener and setOnItemLongClickListener if you want to handle long clicks, which it seems you do.
In the endTouch routine, you need to set mLongPressRunnable to null, so it will be created for the next touch (or lose the null check in startLongPressCheck and handle concurency issues another way).
The runnable created in startLongPressCheck needs to call endTouch when complete to reset everything to the right, nothing pending, state after the long press time period.
In the onTouchEvent switch statement, it is important to not call your endTouch routine, because this means that movement events may stop the click.
There is a suggestion (see Android onClick method doesn't work on a custom view) that if you don't call super.onTouchEvent(event) in your onTouchEvent it can cause problems. This would look something like:
#Override
public boolean onTouchEvent(MotionEvent event) {
if (getChildCount() == 0) {
return super.onTouchEvent(event);
}
boolean handled = false;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startTouch(event);
handled = true;
break;
case MotionEvent.ACTION_UP:
if (mTouchState == TOUCH_STATE_CLICK) {
clickChildAt((int)event.getX(), (int)event.getY());
handled = true;
}
endTouch();
break;
default:
// Do not do anything dramatic here because the system
// may send different (eg. motion) information here which
// may lead to you losing valid clicks if you simply cancel
// the click in process.
break;
}
if (handled == false) handled = super.onTouchEvent(event);
return handled;
}