I need some component or github link to show a seekbar (or progress bar) of a video, and show a thumbnail of the video at the time set (set by the seekbar)
(like youtube)
Thanks a lot!
Ok, I Figured out myself, and I uploaded to:
https://github.com/CorradiSebastian/SeekBarPreview
XML:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.sebastiancorradi.seekbar.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Coded By Sebastian Corradi SebastianCorradi#gmail.com"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:textColor="#AAAAAA"
android:textSize="10dp"
app:layout_constraintTop_toTopOf="parent" />
<com.sebastiancorradi.seekbar.components.SeekBarPreview
android:id="#+id/seekBarPreviewSDCard"
android:layout_height="wrap_content"
android:layout_width="match_parent"/>
</android.support.constraint.ConstraintLayout>
.JAVA:
public class SeekBarPreview extends LinearLayout {
private SeekBar mSeekBar;
private ImageView mPreview;
private View rootView;
private int totalDuration;
private int currentPosition;
private MediaMetadataRetriever retriever;
public SeekBarPreview(Context context) {
super(context);
init();
}
public SeekBarPreview(Context context, AttributeSet attrs)
{
super(context, attrs);
init();
}
public SeekBarPreview(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init(){
LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
rootView = inflater.inflate(R.layout.seekbarpreview_layout, this, true);
mPreview = rootView.findViewById(R.id.imageView);
mSeekBar = rootView.findViewById(R.id.seekBar);
retriever = new MediaMetadataRetriever();
mPreview.setScaleType(ImageView.ScaleType.CENTER_CROP);
}
public void setUrl(String url){
retriever.setDataSource(url, new HashMap<String, String>());
String duration = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
if (duration != null) {
totalDuration = Integer.valueOf(duration);//milisecs
} else {
totalDuration = 0;
//or throw an exception
}
}
public void setUri(Uri uri){
retriever.setDataSource(getContext(),uri);
String duration = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
if (duration != null) {
totalDuration = Integer.valueOf(duration);//milisecs
} else {
totalDuration = 0;
//or throw an exception
}
}
public void update(int i) {
Long newPosition = totalDuration * i * 10L;
Bitmap bitmap = retriever.getFrameAtTime(newPosition);
mPreview.setImageBitmap(bitmap);
int x = mSeekBar.getThumb().getBounds().centerX();
int newPos = x - (i * mPreview.getWidth() / 100);
mPreview.setX(newPos);
}
public void initialize(){
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
update(i);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
mSeekBar.setProgress(1);
}
}
And in the parent you just have to add, MXL:
<com.sebastiancorradi.seekbar.components.SeekBarPreview
android:id="#+id/seekBarPreviewSDCard"
android:layout_height="wrap_content"
android:layout_width="match_parent"/>
JAVA:
SeekBarPreview seekBarPreviewSDCard = findViewById(R.id.seekBarPreviewSDCard);
String path = "android.resource://" + getPackageName() + "/" + R.raw.bunny;
seekBarPreviewSDCard.setUri(Uri.parse(path));
seekBarPreviewSDCard.initialize();
I think you should use this GitHub library for preview images when you seek SeekBar
https://github.com/rubensousa/PreviewSeekBar
Hope this help you...if you need any help you can ask
Related
I have CustomView with 2 RelativeLayouts. There are RecyclerViews in each RelativeLayout. When I add new element to RecyclerView it doesn`t change height.
If I change screen orientation then android measures it well. So my question is how to programmatically tell android that he needs remeasure both childs and parent elements.
requestlayout() and invalidate() doesn`t work
CustomView:
public class ExpandableView extends LinearLayout {
private Settings mSettings ;
private int mExpandState;
private ValueAnimator mExpandAnimator;
private ValueAnimator mParentAnimator;
private AnimatorSet mExpandScrollAnimatorSet;
private int mExpandedViewHeight;
private boolean mIsInit = true;
private int defaultHeight;
private boolean isAllowedExpand = false;
private ScrolledParent mScrolledParent;
private OnExpandListener mOnExpandListener;
public ExpandableView(Context context) {
super(context);
init(null);
}
public ExpandableView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public ExpandableView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private void init(AttributeSet attrs) {
Log.w("tag", "init");
setOrientation(VERTICAL);
this.setClipChildren(false);
this.setClipToPadding(false);
mExpandState = ExpandState.PRE_INIT;
mSettings = new Settings();
if(attrs!=null) {
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.ExpandableView);
mSettings.expandDuration = typedArray.getInt(R.styleable.ExpandableView_expDuration, Settings.EXPAND_DURATION);
mSettings.expandWithParentScroll = typedArray.getBoolean(R.styleable.ExpandableView_expWithParentScroll,false);
mSettings.expandScrollTogether = typedArray.getBoolean(R.styleable.ExpandableView_expExpandScrollTogether,true);
typedArray.recycle();
}
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Log.w("tag", "onMeasure");
Log.w("tag", "widthMeasureSpec - " + widthMeasureSpec);
Log.w("tag", "heightMeasureSpec - " + heightMeasureSpec);
int childCount = getChildCount();
if(childCount!=2) {
throw new IllegalStateException("ExpandableLayout must has two child view !");
}
if(mIsInit) {
((MarginLayoutParams)getChildAt(0).getLayoutParams()).bottomMargin=0;
MarginLayoutParams marginLayoutParams = ((MarginLayoutParams)getChildAt(1).getLayoutParams());
marginLayoutParams.bottomMargin=0;
marginLayoutParams.topMargin=0;
marginLayoutParams.height = 0;
mExpandedViewHeight = getChildAt(1).getMeasuredHeight();
defaultHeight = mExpandedViewHeight;
mIsInit =false;
mExpandState = ExpandState.CLOSED;
View view = getChildAt(0);
if (view != null){
view.setOnClickListener(v -> toggle());
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
Log.w("tag", "onSizeChanged");
if(mSettings.expandWithParentScroll) {
mScrolledParent = Utils.getScrolledParent(this);
}
}
private int getParentScrollDistance () {
int distance = 0;
Log.w("tag", "getParentScrollDistance");
if(mScrolledParent == null) {
return distance;
}
distance = (int) (getY() + getMeasuredHeight() + mExpandedViewHeight - mScrolledParent.scrolledView.getMeasuredHeight());
for(int index = 0; index < mScrolledParent.childBetweenParentCount; index++) {
ViewGroup parent = (ViewGroup) getParent();
distance+=parent.getY();
}
return distance;
}
private void verticalAnimate(final int startHeight, final int endHeight ) {
int distance = getParentScrollDistance();
final View target = getChildAt(1);
mExpandAnimator = ValueAnimator.ofInt(startHeight,endHeight);
mExpandAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
target.getLayoutParams().height = (int) animation.getAnimatedValue();
target.requestLayout();
}
});
mExpandAnimator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if(endHeight-startHeight < 0) {
mExpandState = ExpandState.CLOSED;
if (mOnExpandListener != null) {
mOnExpandListener.onExpand(false);
}
} else {
mExpandState=ExpandState.EXPANDED;
if(mOnExpandListener != null) {
mOnExpandListener.onExpand(true);
}
}
}
});
mExpandState=mExpandState==ExpandState.EXPANDED?ExpandState.CLOSING :ExpandState.EXPANDING;
mExpandAnimator.setDuration(mSettings.expandDuration);
if(mExpandState == ExpandState.EXPANDING && mSettings.expandWithParentScroll && distance > 0) {
mParentAnimator = Utils.createParentAnimator(mScrolledParent.scrolledView, distance, mSettings.expandDuration);
mExpandScrollAnimatorSet = new AnimatorSet();
if(mSettings.expandScrollTogether) {
mExpandScrollAnimatorSet.playTogether(mExpandAnimator,mParentAnimator);
} else {
mExpandScrollAnimatorSet.playSequentially(mExpandAnimator,mParentAnimator);
}
mExpandScrollAnimatorSet.start();
} else {
mExpandAnimator.start();
}
}
public void setExpand(boolean expand) {
if (mExpandState == ExpandState.PRE_INIT) {return;}
getChildAt(1).getLayoutParams().height = expand ? mExpandedViewHeight : 0;
requestLayout();
mExpandState=expand?ExpandState.EXPANDED:ExpandState.CLOSED;
}
public boolean isExpanded() {
return mExpandState==ExpandState.EXPANDED;
}
public void toggle() {
if (isAllowedExpand){
if(mExpandState==ExpandState.EXPANDED) {
close();
}else if(mExpandState==ExpandState.CLOSED) {
expand();
}
}
}
public void expand() {
verticalAnimate(0,mExpandedViewHeight);
}
public void close() {
verticalAnimate(mExpandedViewHeight,0);
}
public interface OnExpandListener {
void onExpand(boolean expanded) ;
}
public void setOnExpandListener(OnExpandListener onExpandListener) {
this.mOnExpandListener = onExpandListener;
}
public void setExpandScrollTogether(boolean expandScrollTogether) {
this.mSettings.expandScrollTogether = expandScrollTogether;
}
public void setExpandWithParentScroll(boolean expandWithParentScroll) {
this.mSettings.expandWithParentScroll = expandWithParentScroll;
}
public void setExpandDuration(int expandDuration) {
this.mSettings.expandDuration = expandDuration;
}
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
Log.w("tag", "onDetachedFromWindow");
if(mExpandAnimator!=null&&mExpandAnimator.isRunning()) {
mExpandAnimator.cancel();
mExpandAnimator.removeAllUpdateListeners();
}
if(mParentAnimator!=null&&mParentAnimator.isRunning()) {
mParentAnimator.cancel();
mParentAnimator.removeAllUpdateListeners();
}
if(mExpandScrollAnimatorSet!=null) {
mExpandScrollAnimatorSet.cancel();
}
}
public void setAllowedExpand(boolean allowedExpand) {
isAllowedExpand = allowedExpand;
}
public void increaseDistance(int size){
if(mExpandState==ExpandState.EXPANDED) {
close();
}
mExpandedViewHeight = defaultHeight + size;
}
//func just for loggs
public void showParams(){
RelativeLayout relativeLayout = (RelativeLayout) getChildAt(1);
/*relativeLayout.requestLayout();
relativeLayout.invalidate();*/
RecyclerView recyclerView = (RecyclerView) relativeLayout.getChildAt(1);
Log.d("tag", "height - " + relativeLayout.getHeight());
Log.d("tag", "childs - " + relativeLayout.getChildCount());
Log.d("tag", "recycler height - " + recyclerView.getHeight());
recyclerView.requestLayout();
recyclerView.invalidateItemDecorations();
recyclerView.invalidate();
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
layoutManager.getHeight();
Log.d("tag", " layoutManager.getHeight() - " + layoutManager.getHeight());
layoutManager.requestLayout();
layoutManager.generateDefaultLayoutParams();
layoutManager.onItemsChanged(recyclerView);
Log.d("tag", " layoutManager.getHeight()2- " + layoutManager.getHeight());
layoutManager.getChildCount();
recyclerView.getChildCount();
Log.d("tag", "manager childs - " + layoutManager.getChildCount());
Log.d("tag", "recycler childs - " + recyclerView.getChildCount());
}
}
Layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="#color/grey_light_color"
>
<com.example.develop.project.Utils.ExpandableView.ExpandableView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/test_custom_view"
app:expWithParentScroll="true"
android:layout_gravity="center"
android:background="#color/grey_color"
>
<android.support.v7.widget.CardView
android:id="#+id/start_card"
android:layout_width="match_parent"
android:layout_height="70dp"
android:background="#color/white_color"
android:layout_marginTop="5dp"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/stage_tv4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#color/grey_deep_color"
android:text="Запуск"
android:layout_centerVertical="true"
android:layout_marginStart="15dp"
android:textSize="18sp"
/>
</RelativeLayout>
</android.support.v7.widget.CardView>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/start_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/start_accept"
android:textSize="18sp"
android:layout_marginStart="15dp"
android:layout_marginTop="15dp"
android:layout_marginBottom="15dp"
android:textColor="#color/grey_deep_color"
android:layout_marginEnd="15dp"
/>
<android.support.v7.widget.RecyclerView
android:id="#+id/my_test_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/start_tv"
android:layout_marginTop="10dp"
android:layout_marginStart="15dp"
android:layout_marginEnd="15dp"
>
</android.support.v7.widget.RecyclerView>
</RelativeLayout>
</com.example.develop.project.Utils.ExpandableView.ExpandableView>
<Button
android:id="#+id/add_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentStart="true"
android:text="Add elem"
android:layout_marginStart="15dp"
android:layout_marginBottom="15dp"
/>
<Button
android:id="#+id/check_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:text="Check params"
android:layout_marginBottom="15dp"
android:layout_marginEnd="15dp"
/>
</RelativeLayout>
Activity:
public class TestActivity extends MvpAppCompatActivity implements TestContract.View {
TestAdapter testAdapter;
#InjectPresenter
public TestPresenter presenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test_layout);
init();
}
private void init() {
ExpandableView expandableView = findViewById(R.id.test_custom_view);
expandableView.setAllowedExpand(true);
Button add_btn = findViewById(R.id.add_btn);
Button check_btn = findViewById(R.id.check_btn);
add_btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
presenter.addElem();
}
});
check_btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
expandableView.showParams();
}
});
}
#Override
public void addElems(ArrayList<String> list) {
testAdapter.notifyDataSetChanged();
}
#Override
public void populateAdapter(ArrayList<String> list) {
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
RecyclerView recyclerView = findViewById(R.id.my_test_tv);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setNestedScrollingEnabled(false);
recyclerView.setHasFixedSize(false);
testAdapter = new TestAdapter(list);
recyclerView.setAdapter(testAdapter);
}
}
Adapter:
public class TestAdapter extends RecyclerView.Adapter<TestAdapter.TasksViewHolder> {
private List<String> list;
public TestAdapter(List<String> list) {
this.list = list;
}
#NonNull
#Override
public TestAdapter.TasksViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.test_item, parent, false);
TestAdapter.TasksViewHolder vh = new TestAdapter.TasksViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(#NonNull TestAdapter.TasksViewHolder holder, int position) {
String text = list.get(position);
holder.textView.setText(text);
}
public static class TasksViewHolder extends RecyclerView.ViewHolder {
private TextView textView;
public TasksViewHolder(View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.my_test_tv);
}
}
#Override
public int getItemCount() {
return list.size();
}
}
The ExpandableView contains some suspicious code in its onMeasure method.
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
...
if(mIsInit) {
((MarginLayoutParams)getChildAt(0).getLayoutParams()).bottomMargin=0;
MarginLayoutParams marginLayoutParams = ((MarginLayoutParams)getChildAt(1).getLayoutParams());
marginLayoutParams.bottomMargin=0;
marginLayoutParams.topMargin=0;
marginLayoutParams.height = 0;
mExpandedViewHeight = getChildAt(1).getMeasuredHeight();
defaultHeight = mExpandedViewHeight;
mIsInit =false;
mExpandState = ExpandState.CLOSED;
View view = getChildAt(0);
if (view != null){
view.setOnClickListener(v -> toggle());
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
The mIsInit variable is set to true on creation and than to false when onMeasure is called for the first time. So the code in the condition runs only once.
But it stores value mExpandedViewHeight obtained as getChildAt(1).getMeasuredHeight() and that is current height of your relative layout, which contains (still empty) RecyclerView.
As far as I can see there's nothing in ExpadableView's code that would update this value, when you add an item to the Recylerview.
I am not sure, what would the correct/perfect implementation of the onMeasure method be (for your component). That would require some debugging and testing of the component, and perhaps some tweaks in other parts of the code.
If you wrote the component, you may want to invest more effort into debugging. If you didn't write the component, you should try to find other one, that is properly implemented. Custom components with custom measurements are an advanced topic.
If you really want to go with debugging and fixing your component, that first thing to do is to update the mExpandedViewHeight value on every measurement, but that might require updating other values that are derived from this value.
This question already has answers here:
Discrete SeekBar
(2 answers)
Closed 5 years ago.
i achieved tooltip design using this link as reference, now i want to achieved seekbar interval as vertical line and not as dots with numbers as like above the image.
Check this code it may help you
public class DWRulerSeekbar extends RelativeLayout implements SeekBar.OnSeekBarChangeListener {
private int seekbarMinValue = 0;
private int seekbarMaxValue = 50;
private SeekBar seekBar;
private Context context;
private OnDWSeekBarListener listener;
private LayoutParams lp;
public interface OnDWSeekBarListener {
public void onStopSeekbarValue(double value);
}
public DWRulerSeekbar(Context context) {
super(context);
this.context = context;
init();
}
public DWRulerSeekbar(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
init();
}
public DWRulerSeekbar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
init();
}
public DWRulerSeekbar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
this.context = context;
init();
}
private void init() {
getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
if (getHeight() > 0) {
addCompoenet();
getViewTreeObserver().removeOnPreDrawListener(this);
}
return false;
}
});
}
private void addCompoenet() {
removeAllViews();
LineRulerView lineRulerView = new LineRulerView(context);
LayoutParams rulerLayoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getHeight() / 3 * 2);
rulerLayoutParams.setMargins(0, 5, 0, 0);
rulerLayoutParams.addRule(CENTER_IN_PARENT);
lineRulerView.setLayoutParams(rulerLayoutParams);
lineRulerView.setMinMaxValue(4, 21);
lineRulerView.setBackgroundColor(Color.TRANSPARENT);
addView(lineRulerView);
DisplayMetrics metrics = getResources().getDisplayMetrics();
int densityDpi = (int)(metrics.density * 160f);
seekBar = new SeekBar(context);
lp = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 60);
seekBar.setThumb(getResources().getDrawable(R.drawable.green_round_circlebg));
seekBar.setProgressDrawable(getResources().getDrawable(R.drawable.seekbar));
seekBar.setProgress(0);
seekBar.setVisibility(View.VISIBLE);
seekBar.setBackgroundColor(Color.TRANSPARENT);
seekBar.setLayoutParams(lp);
seekBar.setMax(15);
seekBar.setOnSeekBarChangeListener(this);
addView(seekBar);
}
public DWRulerSeekbar setDWRulerSeekbarListener(OnDWSeekBarListener listener) {
this.listener = listener;
return this;
}
public DWRulerSeekbar setMinMaxValue(int seekbarMinValue, int seekbarMaxValue) {
this.seekbarMinValue = seekbarMinValue;
this.seekbarMaxValue = seekbarMaxValue;
addCompoenet();
return this;
}
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (listener != null) {
listener.onStopSeekbarValue(seekbarMinValue + progress);
}
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
}
and in xml you have to add this
<com.xelpmoc.zommodity.customseekbar.DWRulerSeekbar
android:id="#+id/dwRulerSeekbar"
android:layout_width="match_parent"
android:layout_marginTop="10dp"
android:layout_height="75dp">
</com.xelpmoc.zommodity.customseekbar.DWRulerSeekbar>
To get the values from seekbar add this in activity
private void initDWSeekbar() {
seekbar_ruler = (DWRulerSeekbar) findViewById(R.id.dwRulerSeekbar);
seekbar_ruler
.setMinMaxValue((int) MIN_VALUE, (int) MAX_VALUE)
.setDWRulerSeekbarListener(new DWRulerSeekbar.OnDWSeekBarListener() {
#Override
public void onStopSeekbarValue(double value) {
timeslorval=value;
}
});
}
seekbar.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:height="5dp" android:gravity="center"
android:id="#android:id/background"
android:drawable="#drawable/seek_bar_background"/>
<item android:height="5dp" android:gravity="center"
android:id="#android:id/progress"
android:drawable="#drawable/seek_bar_progress" />
</layer-list>
and green_round_circlebg.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:width="20dp" android:height="20dp">
<shape android:shape="oval" >
<solid android:color="#2CC285"></solid>
<stroke android:color="#000000" android:width="1dp"></stroke>
</shape>
</item>
</layer-list>
I just followed this tutorial, to create a custom View as an item of a GridLayout.
That's my CustomView
public class RowView extends View{
boolean touchOn;
boolean mDownTouch = false;
private OnToggledListener toggledListener;
int _IdRow = 0;
int _IdColumn = 0;
public RowView(Context context, int Rows, int Columns) {
super(context);
this._IdRow = Rows;
this._IdColumn = Columns;
init();
}
public RowView(Context context) {
super(context);
init();
}
public RowView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public RowView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
touchOn = false;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
MeasureSpec.getSize(heightMeasureSpec));
}
#Override
protected void onDraw(Canvas canvas) {
if (touchOn) {
canvas.drawColor(Color.RED);
} else {
canvas.drawColor(Color.GRAY);
}
}
//onClick not possible to use on custom View so, onTouchEvent is the solution
#Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
switch (event.getAction()) {
//if Click
case MotionEvent.ACTION_DOWN:
touchOn = !touchOn;
invalidate();
if(toggledListener != null){
toggledListener.OnToggled(this, touchOn);
}
mDownTouch = true;
return true;
case MotionEvent.ACTION_UP:
if (mDownTouch) {
mDownTouch = false;
performClick();
return true;
}
}
return false;
}
#Override
public boolean performClick() {
super.performClick();
return true;
}
public void setOnToggledListener(OnToggledListener listener){
toggledListener = listener;
}
public int get_IdRow() {
return _IdRow;
}
public int get_IdColumn() {
return _IdColumn;
}
On this class I can detect when user clicks on an item of GridLayout and change it to another color, that's ok.
But the problem comes at the time to create this :
This is my MainActivity where I show the GridLayout :
int numOfCol = mGridLayout.getColumnCount();
int numOfRow = mGridLayout.getRowCount();
mRowViews = new RowView[numOfCol*numOfRow];
for(int yPos=0; yPos<numOfRow; yPos++){
for(int xPos=0; xPos<numOfCol; xPos++){
RowView tView = new RowView(this, xPos, yPos);
tView.setOnToggledListener(this);
mRowViews[yPos*numOfCol + xPos] = tView;
mGridLayout.addView(tView);
}
}
mGridLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener(){
#Override
public void onGlobalLayout() {
final int MARGIN = 5;
int pWidth = mGridLayout.getWidth();
int pHeight = mGridLayout.getHeight();
int numOfCol = mGridLayout.getColumnCount();
int numOfRow = mGridLayout.getRowCount();
int w = pWidth/numOfCol;
int h = pHeight/numOfRow;
for(int yPos=0; yPos<numOfRow; yPos++){
for(int xPos=0; xPos<numOfCol; xPos++){
GridLayout.LayoutParams params =
(GridLayout.LayoutParams)mRowViews[yPos*numOfCol + xPos].getLayoutParams();
params.width = w - 2*MARGIN;
params.height = h - 2*MARGIN;
params.setMargins(MARGIN, MARGIN, MARGIN, MARGIN);
mRowViews[yPos*numOfCol + xPos].setLayoutParams(params);
}
}
}});
Also there is a method of the Interface OnToggledListener that gives to me the row and column of my GridLayout when an item of it is clicked :
#Override
public void OnToggled(MyView v, boolean touchOn) {
//get the id string
String idString = v.get_IdRow() + ":" + v.get_IdColumn();
}
I'd like to avoid to create that mGridLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() because it fills on the screen thing that I don't want... I tried to put GridLayout 6x6 with android:layout_height="400dp" and it only show 3x3 and this is the LogCat message
D/android.widget.GridLayout: vertical constraints: y6-y0>=1749, y6-y5<=291, y5-y4<=291, y4-y3<=291, y3-y2<=291, y2-y1<=291, y1-y0<=291 are inconsistent; permanently removing: y6-y5<=291.
I'd like to do something like GridLayout[row][colum] to get the color of background and then do stuff, but I'm not able to find this solution.
For simplifying, you can implement a custom Board view wrapping the GridLayout and related logic. Below I report a possible approach.
Expectation here is to have an ItemView for representing one single cell in the board.
public class Board extends FrameLayout implements View.OnClickListener {
private GridLayout mGridView;
private int mRowsCount;
private int mColsCount;
private int mCellSpace;
private OnItemClickListener mOnItemClickListener;
public Board(Context context) {
super(context);
init(context, null);
}
// other constructors
private void init(Context context, AttributeSet attrs) {
// default values
mRowsCount = 1;
mColsCount = 1;
View layout = inflate(getContext(), R.layout.view_lights_board, null);
mGridView = (GridLayout) layout.findViewById(R.id.view_grid);
mGridView.setRowCount(mRowsCount);
mGridView.setColumnCount(mColsCount);
mGridView.post(new Runnable() {
#Override
public void run() {
int width = getMeasuredWidth() / getColumnsCount();
int height = getMeasuredHeight() / getRowsCount();
for (int i = 0; i < getRowsCount(); i++) {
for (int j = 0; j < getColumnsCount(); j++) {
GridLayout.LayoutParams params = (GridLayout.LayoutParams)
getChildAt(i, j).getLayoutParams();
params.width = width;
params.height = height;
getChildAt(i, j).setLayoutParams(params);
}
}
}
});
addView(layout);
}
// this method allows to dinamically create grid
public void buildChildren(int rowsCount, int colsCount) {
mRowsCount = rowsCount;
mColsCount = colsCount;
mGridView.setRowCount(mRowsCount);
mGridView.setColumnCount(mColsCount);
buildChildren();
}
public void buildChildren() {
for (int i = 0; i < getRowsCount(); i++) {
for (int j = 0; j < getColumnsCount(); j++) {
ItemView view = new ItemView(getContext(), i, j);
view.setOnClickListener(this);
mGridView.addView(view);
}
}
}
public void setOnItemClickListener(OnItemClickListener listener) {
mOnItemClickListener = listener;
}
public ItemView getChildAt(int rowIndex, int columnIndex) {
int index = (getColumnsCount() * rowIndex) + columnIndex;
return (ItemView) mGridView.getChildAt(index);
}
public boolean isTouchOn(int rowIndex, int columnIndex) {
return getChildAt(rowIndex, columnIndex).isTouchOn();
}
public int getColumnsCount() {
return mGridView.getColumnCount();
}
public int getRowsCount() {
return mGridView.getRowCount();
}
#Override
public void onClick(View v) {
if (v instanceof ItemView) {
ItemView view = (ItemView) v;
if (mOnItemClickListener != null) {
mOnItemClickListener.onItemClick(view);
}
}
}
public interface OnItemClickListener {
void onItemClick(ItemView view);
}
}
In your Activity layout you will have something like this (here I assume your app package is com.android.example):
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.android.example.Board
android:id="#+id/grid"
android:layout_width="match_parent"
android:layout_height="400dp" />
</FrameLayout>
And this is possible implementation of the Activity:
public class MainActivity extends AppCompatActivity implements LightsOutBoard.OnItemClickListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Board board = (Board) findViewById(R.id.grid);
board.setOnItemClickListener(this);
board.buildChildren(3, 3);
}
#Override
public void onItemClick(ItemView view) {
String text = view.getRowIndex() + " - " + view.getColumnIndex();
Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
}
}
Hope this could help.
I have a next problem, which happens in 2 cases:
First case.
1). I have some custom veiw which draw photos on it with different opacity. here is method MyView.onDraw:
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.getClipBounds(clipRect);
int i1 = Math.min(testColors.length-1, (int)Math.floor(posX/PHOTO_DISTANCE));
int c1 = testColors[Math.max(0, i1)];
int i2 = Math.min(testColors.length-1, (int)Math.ceil(posX/PHOTO_DISTANCE));
int c2 = testColors[Math.max(0, i2)];
paint.setColor(c1);
float r = (255f/PHOTO_DISTANCE*posX)%255;
paint.setAlpha(255);
if(photoA != null){//bitmap != null
bitmapRect.set(0, 0, photoA.getWidth(), photoA.getHeight());
canvas.drawBitmap(photoA, bitmapRect, clipRect, paint);
}
paint.setAlpha((int)(r));
if(photoB != null){//bitmap != null
bitmapRect.set(0, 0, photoB.getWidth(), photoB.getHeight());
canvas.drawBitmap(photoB, bitmapRect, clipRect, paint);
}
}
testColors - array of colors(int);
photoA, photoB - bitmaps;
i1, i2 - image indexes;
c1, c2 - colors. they are not importatant.
I added this view to FrameView:
viewHolder.myFrame.addView(viewHolder.myView, 0);
And in this FrameView I have some clickable RelativeLayout's:
<com.app.custom.view.ClickableRelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/share_action_container"
app1:pressedStateColor="#color/app_pressed_default"
app1:unpressedStateColor="#color/color_white"
android:background="#color/transparent"
android:layout_centerHorizontal="true">
<RelativeLayout android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/circle_background"
android:id="#+id/share_icon"
android:layout_centerHorizontal="true">
<ImageView
android:id="#+id/img_share"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:tag="icon"
android:src="#drawable/ic_share"/>
</RelativeLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_below="#id/share_icon"
android:text="Share"
android:layout_marginTop="3dp"
android:shadowColor="#color/text_shadow"
android:shadowDx="#integer/shadowDX"
android:shadowDy="#integer/shadowDY"
android:shadowRadius="#integer/shadowRadius"
android:background="#color/transparent"
android:layout_centerHorizontal="true"
android:textSize="#dimen/icon_text_size"
android:tag="text"
android:textColor="#android:color/white"
android:id="#+id/txt_share_action"/>
</com.app.custom.view.ClickableRelativeLayout>
Here is a Java code of ClickableRelativeLayout:
public class ClickableRelativeLayout extends RelativeLayout implements View.OnTouchListener {
private ViewHolder viewHolder;
private int pressedStateColor;
private int unpressedStateColor;
private final int DEFAULT_PRESSED_STATE_COLOR;
private final int DEFAULT_UNPRESSED_STATE_COLOR;
public ClickableRelativeLayout(Context context) {
super(context);
DEFAULT_PRESSED_STATE_COLOR = context.getResources().getColor(R.color.app_pressed_default);
DEFAULT_UNPRESSED_STATE_COLOR = context.getResources().getColor(R.color.app_blue_without_transparent);
setup();
initColors(context, null);
}
public ClickableRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
DEFAULT_PRESSED_STATE_COLOR = context.getResources().getColor(R.color.app_pressed_default);
DEFAULT_UNPRESSED_STATE_COLOR = context.getResources().getColor(R.color.app_blue_without_transparent);
setup();
initColors(context, attrs);
}
public ClickableRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
DEFAULT_PRESSED_STATE_COLOR = context.getResources().getColor(R.color.app_pressed_default);
DEFAULT_UNPRESSED_STATE_COLOR = context.getResources().getColor(R.color.app_blue_without_transparent);
setup();
initColors(context, attrs);
}
private void setup(){
setOnTouchListener(this);
}
private void initColors(Context context, AttributeSet attrs){
if(attrs != null) {
TypedArray styledAttributes = context.obtainStyledAttributes(attrs, R.styleable.ClickableRelativeLayout);
pressedStateColor = styledAttributes.getColor(R.styleable.ClickableRelativeLayout_pressedStateColor,
DEFAULT_PRESSED_STATE_COLOR);
unpressedStateColor = styledAttributes.getColor(R.styleable.RowLayout_android_verticalSpacing,
DEFAULT_UNPRESSED_STATE_COLOR);
styledAttributes.recycle();
}else{
pressedStateColor = DEFAULT_PRESSED_STATE_COLOR;
unpressedStateColor = DEFAULT_UNPRESSED_STATE_COLOR;
}
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
viewHolder = new ViewHolder(
(TextView) findViewWithTag("text"),
(ImageView) findViewWithTag("icon")
);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
if (hasOnClickListeners()) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
select();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
unSelect();
break;
}
}
}
return false;
}
private void select(){
if(isInitializedCorrect()){
final int color = pressedStateColor;
viewHolder.text.setTextColor(color);
ImageHelper.INSTANCE.applyColorFilterToImage(viewHolder.icon.getDrawable(), color);
}
}
private void unSelect(){
if(isInitializedCorrect()){
final int color = unpressedStateColor;
viewHolder.text.setTextColor(color);
viewHolder.icon.setColorFilter(color);
}
}
private boolean isInitializedCorrect(){
return viewHolder != null && viewHolder.icon != null && viewHolder.text != null;
}
private class ViewHolder{
ImageView icon;
TextView text;
public ViewHolder(TextView text, ImageView icon) {
this.text = text;
this.icon = icon;
}
}
}
And when I clicked on this layout, background of MyView shrinks, and sets to this ClickableRelativeLayout:
And Second case.
I have some text view on same frame, it is invisible by default, and when you scrolled 10 photos, I apply AlphaAnimation for this TextView and it draws slowly. Here is a code of alpha animation:
AlphaAnimation animation1 = new AlphaAnimation(0.0f, 1.0f);
animation1.setDuration(1300);
animation1.setFillAfter(true);
//here is my TextView
viewHolder.gotItView.setVisibility(View.VISIBLE);
viewHolder.gotItView.startAnimation(animation1);
And happens the same this. On background of this TextView appears content of MyView
I have a weird problem, for some reason the android:ellipsize="end" works, but added the point in the middle of the text == centered vertically instead of being aligned to baseline:
I checked for any "center" properties, but there is none of those:
Update:
This is the XML part:
<com.citylifeapps.cups.customviews.CarmelaTextView
android:id="#+id/venue_address"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="#+id/venue_distance"
android:layout_toRightOf="#+id/venue_name"
android:gravity="left"
android:text="#string/placeholder_venue_address"
android:textColor="#color/cups_white"
android:textSize="20sp"
android:textStyle="bold"
android:ellipsize="end"
android:singleLine="true"
android:scrollHorizontally="true"
android:layout_alignBaseline="#+id/venue_name" />
And the custom TextView class:
public class CarmelaTextView extends TextView {
public CarmelaTextView(Context context, AttributeSet attrs) {
super(context, attrs);
setCarmelaTypeface(context);
}
public CarmelaTextView(Context context) {
super(context);
setCarmelaTypeface(context);
}
private void setCarmelaTypeface(Context context) {
if (this.isInEditMode()) return;
Typeface typeface = Typeface.createFromAsset(context.getAssets(), "carmela.ttf");
this.setTypeface(typeface);
}
}
further check shows that if I use a simple TextView the problem disappears,
but there is nothing in the custom TextView that will cause such a behavior.
Does anyone know why this might happen?
Thanks.
It looks like the problem lies within my custom font I'm using for this custom TextView, from the accepted answer here:
Why does TextView in single line elipsized with "end" show boxes?
I'm guessing that I'm facing the same problem but with a different result because the 3 dots (...) U+FEFF glyph for my font is different.
But still if some one found a solution that works for this issue I would be glad if he could share it.
I used this class to resolve this issue
public class EllipsizingTextView extends TextView {
private static final String ELLIPSIS = "...";
public interface EllipsizeListener {
void ellipsizeStateChanged(boolean ellipsized);
}
private final List<EllipsizeListener> ellipsizeListeners = new ArrayList<EllipsizeListener>();
private boolean isEllipsized;
private boolean isStale;
private boolean programmaticChange;
private String fullText;
private int maxLines = -1;
private float lineSpacingMultiplier = 1.0f;
private float lineAdditionalVerticalPadding = 0.0f;
public EllipsizingTextView(Context context) {
super(context);
}
public EllipsizingTextView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, new int[] { android.R.attr.maxLines });
setMaxLines(a.getInt(0, 1));
}
public EllipsizingTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs, new int[] { android.R.attr.maxLines });
setMaxLines(a.getInt(0, 1));
}
public void addEllipsizeListener(EllipsizeListener listener) {
if (listener == null) {
throw new NullPointerException();
}
ellipsizeListeners.add(listener);
}
public void removeEllipsizeListener(EllipsizeListener listener) {
ellipsizeListeners.remove(listener);
}
public boolean isEllipsized() {
return isEllipsized;
}
#Override
public void setMaxLines(int maxLines) {
super.setMaxLines(maxLines);
this.maxLines = maxLines;
isStale = true;
}
public int getMaxLines() {
return maxLines;
}
#Override
public void setLineSpacing(float add, float mult) {
this.lineAdditionalVerticalPadding = add;
this.lineSpacingMultiplier = mult;
super.setLineSpacing(add, mult);
}
#Override
protected void onTextChanged(CharSequence text, int start, int before,
int after) {
super.onTextChanged(text, start, before, after);
if (!programmaticChange) {
fullText = text.toString();
isStale = true;
}
}
#Override
protected void onDraw(Canvas canvas) {
if (isStale) {
super.setEllipsize(null);
resetText();
}
super.onDraw(canvas);
}
private void resetText() {
int maxLines = getMaxLines();
String workingText = fullText;
boolean ellipsized = false;
if (maxLines != -1) {
Layout layout = createWorkingLayout(workingText);
if (layout.getLineCount() > maxLines) {
workingText = fullText.substring(0,
layout.getLineEnd(maxLines - 1)).trim();
while (createWorkingLayout(workingText + ELLIPSIS)
.getLineCount() > maxLines) {
workingText = workingText.substring(0,
workingText.length() - 1 - 1);
}
workingText = workingText + ELLIPSIS;
ellipsized = true;
}
}
if (!workingText.equals(getText())) {
programmaticChange = true;
try {
setText(workingText);
} finally {
programmaticChange = false;
}
}
isStale = false;
if (ellipsized != isEllipsized) {
isEllipsized = ellipsized;
for (EllipsizeListener listener : ellipsizeListeners) {
listener.ellipsizeStateChanged(ellipsized);
}
}
}
private Layout createWorkingLayout(String workingText) {
return new StaticLayout(workingText, getPaint(), getWidth()
- getPaddingLeft() - getPaddingRight(), Alignment.ALIGN_NORMAL,
lineSpacingMultiplier, lineAdditionalVerticalPadding, false);
}
#Override
public void setEllipsize(TruncateAt where) {
// Ellipsize settings are not respected
}
}