I need to create text display like the Confide app. What I tried is to use FlowLayout but then I'm not able to get the row so that I can hide show row wise. There seems to be various options but kinda confused and not able to think wwat exactly to do... Like break the TextView and show in ListView but then I don't know how to form lines and create adapter.
Kindly help me if anyone knows this thing. I tried to search on Google but nothing fruitful.
At present I'm showing TextView using FlowLayout and showing hiding each TextView.
Try creating custom view like this one, lest call it CustomTextView:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="3dp"
android:layout_marginTop="3dp"
android:background="#FF0000"
android:orientation="horizontal"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="3dp"
android:paddingBottom="3dp" >
<TextView
android:id="#+id/label"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible"
android:maxLines="1"
android:textColor="#FFFFFF" />
</LinearLayout>
java code:
public class CustomTextView extends FrameLayout implements View.OnClickListener {
public CustomTextView(Context context) {
super(context);
init(context);
}
public CustomTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public CustomTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
LayoutInflater.from(context).inflate(R.layout.custom_text_view, this);
setOnClickListener(this);
mLabelTextView = (TextView) findViewById(R.id.label);
}
#Override
public void onClick(View v) {
mLabelTextView.setVisibility(View.VISIBLE)
}
}
To add this views to your container view try something like this:
Display display = ((Activity) getContext()).getWindowManager().getDefaultDisplay();
mContainerView.removeAllViewsInLayout();
mContainerView.removeAllViews();
int maxWidth = display.getWidth() - 20;
LinearLayout.LayoutParams params;
LinearLayout newLL = new LinearLayout(getContext());
newLL.setLayoutParams(new LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT));
newLL.setGravity(Gravity.LEFT);
newLL.setOrientation(LinearLayout.HORIZONTAL);
int widthSoFar = 0;
for (CustomTextView customTextView : CustomTextViewList) {
LinearLayout LL = new LinearLayout(getContext());
LL.setOrientation(LinearLayout.HORIZONTAL);
LL.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
LL.setLayoutParams(new ListView.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT));
customTextView.measure(0, 0);
params = new LinearLayout.LayoutParams(customTextView.getMeasuredWidth(), FrameLayout.LayoutParams.WRAP_CONTENT);
LL.addView(customTextView, params);
LL.measure(0, 0);
widthSoFar += customTextView.getMeasuredWidth();
if (widthSoFar >= maxWidth) {
mContainerView.addView(newLL);
newLL = new LinearLayout(getContext());
newLL.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT));
newLL.setOrientation(LinearLayout.HORIZONTAL);
newLL.setGravity(Gravity.LEFT);
params = new LinearLayout.LayoutParams(LL.getMeasuredWidth(), LL.getMeasuredHeight());
newLL.addView(LL, params);
widthSoFar = LL.getMeasuredWidth();
} else {
newLL.addView(LL);
}
}
mContainerView.addView(newLL);
Example of CustomTextViewList (to answer your question from comment)
ArrayList<CustomTextView> CustomTextViewList = new ArrayList<CustomTextView>()
CustomTextViewList.add(new CustomTextView(context));
CustomTextViewList.add(new CustomTextView(context));
CustomTextViewList.add(new CustomTextView(context));
Related
I have a RecyclerView that I want to always have 3 columns. For some reason the width when using a GridLayoutManager seems to be a static width and I'm not sure how to get it to match the screen.
On iOS it is easy as I use a UICollectionView and auto layout manages that like so:
How do I get the same result on Android? Here is my content_symptom_tracker.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"
android:background="#color/bzPrimary"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context=".SymptomTracker"
tools:showIn="#layout/activity_symptom_tracker">
<com.cryptixltd.peterruppert.brainzaps.GridRecyclerView
android:id="#+id/symptomRecycler"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:foregroundGravity="center"
android:layoutAnimation="#anim/grid_layout_animation_from_bottom"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</android.support.constraint.ConstraintLayout>
Here is how I setup the Recycler in the Activity:
recyclerView = findViewById(R.id.symptomRecycler);
int numberOfColumns = 3;
recyclerView.setLayoutManager(new GridLayoutManager(this, numberOfColumns));
adapter = new SymptomAdapter(this, common_symptoms);
recyclerView.setAdapter(adapter);
But it gives me this result, the same width no matter the device:
I don't want to change the size of the icons, just make the spacing of the width match the parent basically.
Thanks!
EDIT: Here is the GridRecyclerView class, it is a custom class to help with the Grid Animations:
public class GridRecyclerView extends RecyclerView {
public GridRecyclerView(Context context) { super(context); }
public GridRecyclerView(Context context, AttributeSet attrs) { super(context, attrs); }
public GridRecyclerView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); }
#Override
protected void attachLayoutAnimationParameters(View child, ViewGroup.LayoutParams params,
int index, int count) {
final LayoutManager layoutManager = getLayoutManager();
if (getAdapter() != null && layoutManager instanceof GridLayoutManager){
GridLayoutAnimationController.AnimationParameters animationParams =
(GridLayoutAnimationController.AnimationParameters) params.layoutAnimationParameters;
if (animationParams == null) {
// If there are no animation parameters, create new once and attach them to
// the LayoutParams.
animationParams = new GridLayoutAnimationController.AnimationParameters();
params.layoutAnimationParameters = animationParams;
}
// Next we are updating the parameters
// Set the number of items in the RecyclerView and the index of this item
animationParams.count = count;
animationParams.index = index;
// Calculate the number of columns and rows in the grid
final int columns = ((GridLayoutManager) layoutManager).getSpanCount();
animationParams.columnsCount = columns;
animationParams.rowsCount = count / columns;
// Calculate the column/row position in the grid
final int invertedIndex = count - 1 - index;
animationParams.column = columns - 1 - (invertedIndex % columns);
animationParams.row = animationParams.rowsCount - 1 - invertedIndex / columns;
} else {
// Proceed as normal if using another type of LayoutManager
super.attachLayoutAnimationParameters(child, params, index, count);
}
}
}
try to set the GridRecyclerView to match constraints.
in your case just make android:layout_width=0dp.
So I've got this problem in my custom view. I'm trying to create a custom RelativeLayout with an infinite scrolling animation. To achieve this, I've created a layout backgroundscrollrelativelayout.xml like so:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/mainTile"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/topTile" />
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/leftTile" />
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/diagonalTile" />
</RelativeLayout>
The idea is that the ImageViews will translate their position on an animation update callback.
I've created the BackgroundScrollRelativeLayout.java like so:
public class BackgroundScrollRelativeLayout extends RelativeLayout {
final int layoutToUse = R.layout.backgroundscrollrelativelayout;
final int mainTileId = R.id.mainTile;
final int leftTileId = R.id.leftTile;
final int topTileId = R.id.topTile;
final int diagonalTileId = R.id.diagonalTile;
final float valueStart = 0.0f;
final float valueEnd = 1.0f;
final long animationDuration = 50000L;
private Context mContext;
private ValueAnimator scrollAnimator;
private ImageView mainTile;
private ImageView leftTile;
private ImageView topTile;
private ImageView diagonalTile;
public BackgroundScrollRelativeLayout(Context context) {
super(context);
mContext = context;
acquireViewsInLayout();
initializeAnimator();
scrollAnimator.start();
}
public BackgroundScrollRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
acquireViewsInLayout();
initializeAnimator();
scrollAnimator.start();
}
public BackgroundScrollRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
acquireViewsInLayout();
initializeAnimator();
scrollAnimator.start();
}
#Override
public void setBackgroundColor(int color) {
// Not supported
}
#Override
public void setBackgroundResource(int resid) {
// Not supported
}
#Override
public void setBackground(Drawable background) {
mainTile.setBackground(background);
leftTile.setBackground(background);
}
/*
#Override
public void setBackgroundDrawable(Drawable background) {
//super.setBackgroundDrawable(background);
//mainTile.setBackground(background);
//leftTile.setBackground(background);
}*/
// Intent: Inflate the layout associated with this View
private void inflateLayout(){
// TO inflateLayout, we connect the inflater to context, then we call inflate the layout associated
// with this view
//LayoutInflater inflater = LayoutInflater.from(mContext);
//inflater.inflate(layoutToUse, this);
inflate(getContext(), layoutToUse, this);
}
// Intent: Find all Views in Layout
private void findAllViewsById(){
mainTile = (ImageView)this.findViewById(mainTileId);
leftTile = (ImageView)this.findViewById(leftTileId);
topTile = (ImageView)this.findViewById(topTileId);
diagonalTile = (ImageView)this.findViewById(diagonalTileId);
}
// Intent: Concretely acquire all Views in Layout
private void acquireViewsInLayout(){
// TO acquireViewsInLayout, we inflate the layout,
// then we find the view of each known view id and save the view
inflateLayout();
findAllViewsById();
}
// Intent: Initialize animator properties
private void initializeAnimator(){
// TO initializeAnimator, we set how the animator will keep track of animation,
// then we set the animation repeat type, then we set the type of interpolation,
// then we set the animation duration, then we apply animation update listener
scrollAnimator = ValueAnimator.ofFloat(valueStart, valueEnd);
scrollAnimator.setRepeatCount(ValueAnimator.INFINITE);
scrollAnimator.setInterpolator(new LinearInterpolator());
scrollAnimator.setDuration(animationDuration);
addScrollAnimatorUpdateListener();
}
// Intent: Add an update listener to the scroll animator
private void addScrollAnimatorUpdateListener(){
// TO addScrollAnimatorUpdateListener, we add an update listener to scroll animator
scrollAnimator.addUpdateListener( new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
// Do something...
updateScrollAnimation();
}
});
}
private void updateScrollAnimation(){
float progress = (float)scrollAnimator.getAnimatedValue();
float widthOfTile = mainTile.getWidth();
float moveInXAxis = widthOfTile * progress;
mainTile.setTranslationX(moveInXAxis);
leftTile.setTranslationX(moveInXAxis - widthOfTile);
// Ignore the rest for now
topTile.setTranslationY(-1.0f);
diagonalTile.setTranslationX(-1.0f);
diagonalTile.setTranslationY(-1.0f);
}
}
I use my custom view like so in an activity:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="com.martianstudio.adivinaque.AppSettingsActivity">
<com.martianstudio.adivinaque.BackgroundScrollRelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/main_activity_animation_icon" />
</RelativeLayout>
The goal of this is to allowing the user to specify the background that they want to scroll with android:background. I do not want the parent RelativeLayout to take on this drawable as a background. Instead, I want the ImageViews to set the drawable as the background. I have overridden the setBackground in my custom view so that only the ImageViews set the drawable as the background and not the RelativeLayout (as it would by default).
#Override
public void setBackground(Drawable background) {
mainTile.setBackground(background);
leftTile.setBackground(background);
}
However, I get this error:
java.lang.NullPointerException at com.martianstudio.adivinaque.BackgroundScrollRelativeLayout.setBackground
This says that mainTile in setBackground has not been set (it is null). In my findAllViewsById() I explicitly write this.mainTile = (ImageView)this.findViewById(mainTileId);, where mainTileId = R.id.mainTile, and I inflate the layout in inflateLayout(). Both of these methods are called in the constructors of the class. It seems to me that for some reason, it cannot find the ImageView with the mainTile Id when I override the setBackground method like I do. If I don't override the setBackground method, I do not get a NullPointerException, however, it defaults to the default setBackground method, which is what I do not want. Am I missing somthing?
Any help will be appreciated :). Thank you!
Here you can see what my problem is, I made a custom TextView in Android, to add stroke to some scores. But so far I'm having 2 separated texts instead of one with stroke...
Here is my code:
XML:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/game_end_share_relative_main"
android:layout_width="#dimen/share_width"
android:layout_height="#dimen/share_height"
android:background="#000000" >
<com.sharing.StrokeTextView
android:id="#+id/user_share_points"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="3"
android:textColor="#color/primary"
android:layout_marginRight="16dp"
style="#style/SecondaryFontFamily"
android:textSize="70dp" />
And the custom TextView:
public class StrokeTextView extends TextView {
private int mStrokeColor;
private int mStrokeWidth;
private TextPaint mStrokePaint;
public StrokeTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
public StrokeTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public StrokeTextView(Context context) {
super(context);
}
public void setStrokeColor(int color) {
mStrokeColor = color;
}
public void setStrokeWidth(int width) {
mStrokeWidth = width;
}
#Override
protected void onDraw(Canvas canvas) {
if (mStrokePaint == null) {
mStrokePaint = new TextPaint();
}
mStrokePaint.setTextSize(getTextSize());
mStrokePaint.setTypeface(getTypeface());
mStrokePaint.setFlags(getPaintFlags());
mStrokePaint.setStyle(Paint.Style.STROKE);
mStrokePaint.setColor(mStrokeColor);
mStrokePaint.setStrokeJoin(Paint.Join.ROUND);
mStrokePaint.setStrokeCap(Paint.Cap.ROUND);
mStrokePaint.setStrokeWidth(mStrokeWidth);
mStrokePaint.setShadowLayer(2.0f, 5.0f, 5.0f, Color.BLACK);
mStrokePaint.setTypeface(Typeface.createFromAsset(getContext().getAssets(), "fonts/MikadoBlack.otf"));
String text = getText().toString();
canvas.drawText(text, getWidth() - (mStrokePaint.measureText(text) / 2), getBaseline(), mStrokePaint);
super.setTypeface(Typeface.createFromAsset(getContext().getAssets(), "fonts/MikadoBlack.otf"));
super.onDraw(canvas);
}
}
Thanks for your help in advance :)
Jose
This is because the drawText in super class is drawing at a different position than yours. Try setting the content gravity to 'center' using View.setGravity(Gravity.CENTER) this may solve your problem. Also, if you are using padding on the view then you need to factor it in while calculating the origin for drawText method
int hPadding = getPaddingLeft()+getPaddingRight();
int contentAreaWidth = getWidth() - hPadding;
canvas.drawText(text, contentAreaWidth - (mStrokePaint.measureText(text) / 2), getBaseline(), mStrokePaint);
This would help in aligning the stroked text with the normal text drawn in the super class.
For instance I have a custom button and want to connect it to a SeekBar:
public class SeekBarButton extends ImageButton {
SeekBar seekBar;
public SeekBarButton(Context context) {
super(context);
}
public SeekBarButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SeekBarButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setSeekBar(SeekBar seekBar) {
this.seekBar = seekBar;
}
public SeekBar getSeekBar() {
return seekBar;
}
}
I can do it in the code:
sbb = (SeekBarButton) rootView.findViewById(R.id.minus_red);
sbRed = (SeekBar) rootView.findViewById(R.id.sbRed);
sbb.setSeekBar(sbRed);
But 8 buttons will give a lot of boilerplate, and I want something like:
<com.whatever.views.SeekBarButton
...
whatToPutHere:seekbar="#+id/sbRed" // like this? whatToPutHere?
android:id="#+id/minus_red" />
<SeekBar
android:id="#+id/sbRed"
... />
The easiest way to do this is to create a custom ViewGroup that contains both the Button and Seekbar. If you cannot do that, for any reason, here's a solution:
There are a few steps to make this work. First you must define a custom XML attribute that you can then reference and use.
Edit (or create) res/values/attrs.xml. Add:
<declare-styleable name="SeekBarButton">
<attr name="seekbarId" format="integer" />
</declare-styleable>
Then, in SeekBarButton, call this from the constructors:
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
if (attrs != null) {
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.SeekBarButton, defStyleAttr, 0);
mSeekbarId = a.getResourceId(R.styleable.SeekBarButton_seekbarId, 0);
a.recycle();
}
}
Finally, in your root ViewGroup of your layout file, add
xmlns:app="http://schemas.android.com/apk/res-auto"
Then,
<com.whatever.views.SeekBarButton
android:id="#+id/minus_red"
app:seekbarId="#+id/sbRed"
... />
<SeekBar
android:id="#+id/sbRed"
... />
Note You will need to call ((ViewGroup) getParent()).findViewById(mSeekbarId) in SeekBarButton to instantiate the SeekBar, but getParent() will be null in SeekBarButton constructors. So, delay findViewById() until you need the SeekBar.
I think you are close. In the first XML tag of your layout file (my example is a RelativeLayout) you need this reference to "custom":
<RelativeLayout
xmlns:custom="http://schemas.android.com/apk/res-auto">
Then farther down wherever your custom ImageButton is, you need this:
<com.whatever.views.SeekBarButton
...
custom:seekbar="#+id/sbRed"
android:id="#+id/minus_red" />
You will also need a seekBarButton.xml file in your project\res\values folder, if you didn't already know that.
<ScrollView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/titleBarBG"
android:layout_alignParentLeft="true" >
<RelativeLayout
android:id="#+id/scrollContent"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<GridView
android:id="#+id/issueList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/archiveTitle"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:background="#drawable/customshape"
android:numColumns="3"
android:overScrollMode="never"
android:scrollbars="none" >
</GridView>
</RelativeLayout>
</ScrollView>
I would like to create a gridview that act like a table . For example, the size of the grid will increase that will make the gridview taller. Instead of hide the extra content ,I would like the grid view show all content and expand the height when there is additional content
How to implement this? thanks
public class MyGridView extends GridView {
public MyGridView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyGridView(Context context) {
super(context);
}
public MyGridView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
}
This is slightly cleaned up version of: Grid of images inside ScrollView
WrappedGridView.java:
/**
* Use this class when you want a gridview that doesn't scroll and automatically
* wraps to the height of its contents
*/
public class WrappedGridView extends GridView {
public WrappedGridView(Context context) {
super(context);
}
public WrappedGridView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public WrappedGridView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Calculate entire height by providing a very large height hint.
// View.MEASURED_SIZE_MASK represents the largest height possible.
int expandSpec = MeasureSpec.makeMeasureSpec(MEASURED_SIZE_MASK, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
ViewGroup.LayoutParams params = getLayoutParams();
params.height = getMeasuredHeight();
}
}
Include in an XML layout like you would a GridLayout. Use an adapter to provide it views.
As far as I can tell, this is the simplest solution available now. There no other view available in the framework that handles wrapping. It would be nice if someone were to provide an elegant, automatically sizing table view. Modifying GridView.java for this purpose may not be a bad idea.
Alternatively, you may find one of the 'FlowLayout' projects acceptable. There is android-flowlayout and FlowLayout. These are a little more flexible than a simple grid and, I assume, a little less efficient. You also shouldn't need to provide them an adapter.