How to unite views in GridView in the center? - android

I am using GridView to create color picker. There is a circle inside each cell. Even if I set wrap content for layout_width and layout_height there is always gap between columns. I want to unite cells in the center or remove these gaps.
Here is my GridView's layout xml.
<?xml version="1.0" encoding="utf-8"?>
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/color_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:numColumns="3" />
CircleView class
public class CircleView extends View {
private static final String TAG = "CircleView";
private Paint mCirclePaint;
private int mCircleColor;
private Paint mStrokePaint;
private int mStrokeColor;
private int mWidthInDp;
private int mHeightInDp;
public CircleView(Context context){
super(context);
mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mCirclePaint.setColor(mCircleColor);
}
#Override
protected void onMeasure(int widthMeasuredSpec, int heightMeasuredSpec) {
Log.d(TAG, "onMeasure: ");
setMeasuredDimension(mWidthInDp, mHeightInDp);
}
public void setLayoutParamsInDp(int width, int height){
this.mWidthInDp = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, width, getResources().getDisplayMetrics());
this.mHeightInDp = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, height, getResources().getDisplayMetrics());
}
public void setCircleColor(int color){
this.mCircleColor = color;
mCirclePaint.setColor(mCircleColor);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int centerX = mWidthInDp / 2;
int centerY = mHeightInDp / 2;
float radius = Math.min(mWidthInDp, mWidthInDp) / 2;
canvas.drawCircle(centerX, centerY, radius, mCirclePaint);
canvas.save();
}
}
GridView adapter
#Override
public View getView(int position, View convertView, ViewGroup viewGroup) {
CircleView circleView = new CircleView(mContext);
circleView.setCircleColor(mColors.get(position));
circleView.setLayoutParamsInDp(50, 50);
return circleView;
}
Here is the output. How can I delete gaps between circles?

I think the problem is in GridView as parent. Add another layout as parent and in there add GridView with width & height as wrap content!

use this
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/color_picker"
android:numColumns="3"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:horizontalSpacing="0dip"
android:verticalSpacing="0dip">
</GridView>

Related

Adding graphics views android layout

This will be my first question, so I apologize for my probable mistakes.
I'm trying to add a red circle each time I press the button I've incorported to my layout. I'd like all circles stayed in the layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<LinearLayout
android:id="#+id/panelJuego"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.76"
android:orientation="horizontal" >
</LinearLayout>
<Button
android:id="#+id/button1"
style="?android:attr/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="botonRojo"
android:text="Button" />
</LinearLayout>
The java code related with my trying is:
public void botonRojo(View v) {
LinearLayout panelJuego = (LinearLayout) findViewById(R.id.panelJuego);
PonCirculo circulo = new PonCirculo(this, 30, 30, "#FF0000");
circulo.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
panelJuego.addView(circulo);
}
public class PonCirculo extends View {
private int radio = 30;
private String color;
public PonCirculo(Context context, int x, int y, String color) {
super(context);
Cx = Cx + x;
Cy = Cy + y;
this.color = color;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.parseColor(color));
canvas.drawCircle(Cx, Cy, radio, paint);
}
Actually each time I press the button a red filled circle appears in the android screen but when I press the button again a new circle appears and the fomer disappears. Can anyone help me? Thanks.
I'm assuming you are just trying to add each circle under each other when the button is pressed.
Currently you are adding a new view each time now but it is over-laying the previous view. You need add the views giving each new circle a unique id. Then place the new circle underneath or whatever position you choose. I'm giving you some example code that I changed to use a relative layout and I place each circle under the previous one. This should get you started with what you are looking for:
Example Layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/relativeCircleLayout">
</RelativeLayout>
<Button
android:id="#+id/button1"
style="?android:attr/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Add Circle"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:onClick="botonRojo"/>
</RelativeLayout>
Example Code:
public class MainActivity extends AppCompatActivity {
ArrayList<PonCirculo> viewList = new ArrayList<PonCirculo>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void botonRojo(View v) {
RelativeLayout panelJuego = (RelativeLayout) findViewById(R.id.relativeCircleLayout);
PonCirculo circulo = new PonCirculo(this, 30, 30, "#FF0000");
circulo.setId(View.generateViewId());
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(100, 100);
if(!viewList.isEmpty()) {
int id = viewList.get(viewList.size()-1).getId();
params.addRule(RelativeLayout.BELOW, id);
}
panelJuego.addView(circulo, params);
viewList.add(circulo);
}
public class PonCirculo extends View {
private int radio = 30;
private String color;
int Cx, Cy;
public PonCirculo(Context context, int x, int y, String color) {
super(context);
Cx = Cx + x;
Cy = Cy + y;
this.color = color;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.parseColor(color));
canvas.drawCircle(Cx, Cy, radio, paint);
}
}
}

Resizing rotated child view in custom ViewGroup subclass

Background
I have a custom ViewGroup subclass that rotates and mirrors its child view. The purpose for this is to correctly display traditional Mongolian text.
I could put anything is this ViewGroup, but for my current project I am putting an EditText in it. (I was never successful in just rotating and mirroring the EditText directly. However, wrapping it in this custom view group does work.)
Problem
My problem is that when I try to resize the ViewGroup programmatically, its child view is not getting resized properly along with it. I would like the EditText to match the size of the parent ViewGroup so that it appears to be a single view.
MCVE
I made a new project to show the problem. The button increases the width of the ViewGroup (shown in red). The images show the project start (with everything working fine) and two width increments. The EditText is white and is not getting resized even though the width and height are set to match_parent
The full project code is below.
MongolViewGroup.java (Custom ViewGroup that rotates and mirrors its content)
public class MongolViewGroup extends ViewGroup {
private int angle = 90;
private final Matrix rotateMatrix = new Matrix();
private final Rect viewRectRotated = new Rect();
private final RectF tempRectF1 = new RectF();
private final RectF tempRectF2 = new RectF();
private final float[] viewTouchPoint = new float[2];
private final float[] childTouchPoint = new float[2];
private boolean angleChanged = true;
public MongolViewGroup(Context context) {
this(context, null);
}
public MongolViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
}
public View getView() {
return getChildAt(0);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final View view = getView();
if (view != null) {
measureChild(view, heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(resolveSize(view.getMeasuredHeight(), widthMeasureSpec),
resolveSize(view.getMeasuredWidth(), heightMeasureSpec));
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (angleChanged) {
final RectF layoutRect = tempRectF1;
final RectF layoutRectRotated = tempRectF2;
layoutRect.set(0, 0, right - left, bottom - top);
rotateMatrix.setRotate(angle, layoutRect.centerX(), layoutRect.centerY());
rotateMatrix.postScale(-1, 1);
rotateMatrix.mapRect(layoutRectRotated, layoutRect);
layoutRectRotated.round(viewRectRotated);
angleChanged = false;
}
final View view = getView();
if (view != null) {
view.layout(viewRectRotated.left, viewRectRotated.top, viewRectRotated.right,
viewRectRotated.bottom);
}
}
#Override
protected void dispatchDraw(Canvas canvas) {
canvas.save();
canvas.rotate(-angle, getWidth() / 2f, getHeight() / 2f);
canvas.scale(-1, 1);
super.dispatchDraw(canvas);
canvas.restore();
}
#Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
invalidate();
return super.invalidateChildInParent(location, dirty);
}
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
viewTouchPoint[0] = event.getX();
viewTouchPoint[1] = event.getY();
rotateMatrix.mapPoints(childTouchPoint, viewTouchPoint);
event.setLocation(childTouchPoint[0], childTouchPoint[1]);
boolean result = super.dispatchTouchEvent(event);
event.setLocation(viewTouchPoint[0], viewTouchPoint[1]);
return result;
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
MongolViewGroup viewGroup;
EditText editText;
int newWidth = 300;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewGroup = (MongolViewGroup) findViewById(R.id.viewGroup);
editText = (EditText) findViewById(R.id.editText);
}
public void buttonClicked(View view) {
newWidth += 200;
ViewGroup.LayoutParams params = viewGroup.getLayoutParams();
params.width=newWidth;
viewGroup.setLayoutParams(params);
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_main"
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.example.mongolviewgrouptest.MainActivity">
<com.example.mongolviewgrouptest.MongolViewGroup
android:id="#+id/viewGroup"
android:layout_width="100dp"
android:layout_height="200dp"
android:background="#color/colorAccent">
<EditText
android:id="#+id/editText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="#android:color/black"
android:background="#android:color/white"/>
</com.example.mongolviewgrouptest.MongolViewGroup>
<Button
android:text="Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/button"
android:onClick="buttonClicked"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"/>
</RelativeLayout>
You're not recalculating viewRectRotated for your EditText when the
ViewGroup's onLayout(...) method is called again.
Since angleChanged is set to false (and never changes) after your ViewGroups first layout, then the part that calculates the left, right, top and bottom values for your EditText
is skipped any time after the first time when your ViewGroup
requestsLayout (when you change its height or width).
As such, your EditText is still laid out with the same left,right,top
and bottom values it was initially laid out with.
Do away with the angleChanged and it should work just fine. Like so:
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
final RectF layoutRect = tempRectF1;
final RectF layoutRectRotated = tempRectF2;
layoutRect.set(0, 0, right - left, bottom - top);
rotateMatrix.setRotate(angle, layoutRect.centerX(), layoutRect.centerY());
rotateMatrix.postScale(-1, 1);
rotateMatrix.mapRect(layoutRectRotated, layoutRect);
layoutRectRotated.round(viewRectRotated);
final View view = getView();
if (view != null) {
view.layout(viewRectRotated.left, viewRectRotated.top, viewRectRotated.right,
viewRectRotated.bottom);
}
}
I've tested this and it works just fine this way.
If you need angleChanged for any reason, then just make sure it's changed back to true inside your ViewGroup's onMeasure method so that viewRectRotated is recalculated again. However I wouldn't recommend that.

customView in LinearLayout fills space

I'm using a custom view MaskCustomView by subclassing View. The width/height layout params for the custom view are both WRAP_CONTENT.
What happens is the custom view in the LinearLayout takes all the space. I can see that using uiautomatorViewer :
Code is :
public class MaskImageView extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mask_image);
LinearLayout container = (LinearLayout) findViewById(R.id.maskID);
MaskCustomView maskView = new MaskCustomView(this);
maskView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT));
container.addView(maskView);
}
public class MaskCustomView extends View {
private Bitmap bp;
private Paint p;
public MaskCustomView(Context context) {
super(context);
bp = BitmapFactory.decodeResource(getResources(), R.drawable.apple);
p = new Paint();
}
#Override
public void onDraw(Canvas canvas) {
canvas.drawBitmap(bp, 0, 0 , p);
super.onDraw(canvas);
}
}
}
xml layout :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:id="#+id/maskID"
android:layout_width="match_parent"
android:layout_height="match_parent">
Shouldn't just wrap content and don't use whole space for both height and width ?
Try to make your MaskCustomView override onMeasure:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if(bp == null) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
} else {
setMeasuredDimension(MeasureSpec.makeMeasureSpec(bp.getWidth(), MeasureSpec.MODE_CONSTANT), MeasureSpec.makeMeasureSpec(bp.getHeight(), MeasureSpec.MODE_CONSTANT);
}
}

Issue in aligning the custom view

I am just trying to implement a customView from scratch i.e by extending the view class and overriding the onDraw() method. Just trying to build a simple view, a view which just draws a circle for now. I am facing some issue in aligning it and i am not able to understand how android is calculating the views dimensions.
Just having only the view i.e setContentView(new MyCustomView(this)) works fine... it takes the entire space and draws the circle. But if i impose any constraints i.e giving margin, or aligning it in centreparent makes my view completely lost and it doesnt draw anything. The issue is the view is getting clipped by its parent but not able to understand why its getting clipped. Any help around this would be greatly appreciated. Here is my code.
Here is my customView
public class MyCustomView extends View {
private Paint myPaint=null;
private boolean useCenters;
private float xCoordinate;
private float yCoordinate;
private float viewWidth;
private float viewHeight;
private int totalTime;
private static float SWEEP_INC ;
private RectF myRect;
private boolean shouldInvalidate;
private float mSweep;
public MyCustomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initPaintComponents();
}
public MyCustomView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public MyCustomView(Context context) {
this(context,null);
}
private void initPaintComponents() {
myPaint = new Paint();
myPaint.setStyle(Paint.Style.STROKE);
myPaint.setStrokeWidth(4);
myPaint.setColor(0x880000FF);
useCenters = false;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
calculateCoordinates();
}
private void calculateCoordinates() {
xCoordinate = getX();
yCoordinate = getY();
viewWidth = getWidth();
viewHeight = getHeight();
myRect = new RectF(xCoordinate+3, yCoordinate+3, xCoordinate+viewWidth-(viewWidth/10), yCoordinate+viewHeight-(viewHeight/10));
Log.i("SAMPLEARC","xcoordinate: "+xCoordinate+" ycoordinate: "+yCoordinate+" view width:"+viewWidth+" view height:"+viewHeight+" measured width: "+getMeasuredWidth()+"measured height:"+getMeasuredHeight());
}
public int getTotalTime() {
return totalTime;
}
public void setTotalTime(int totalTime) {
this.totalTime = totalTime;
SWEEP_INC = (float)6/totalTime;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawArc(myRect, 0, mSweep, useCenters, myPaint);
mSweep += SWEEP_INC;
if(mSweep > 280)
{
myPaint.setColor(0x888800FF);
}
invalidate();
}
}
MyActivity:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
MyCustomView myView = (MyCustomView) findViewById(R.id.customimg);
myView.setTotalTime(10);
}
main.xml
RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#android:color/white"
com.example.anim.MyCustomView android:id="#+id/customimg"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_centerInParent="true"
if i remove that centerInParent in xml it gets drawn. so callling setMeasureDimentions() in onMeasure() doesnt have any affect either. But the xcoodinate,ycoordinate,viewWidth and viewHeight seems to give the correct values. Just need to understand why the view is getting clipped and how android is calculating the dimensions at runtime. And how do i consider the margin paramters while drawing these customViews. Any help would be greatly appreciated.
Thanks in advance
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:lib="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<com.ind.Custom_Attribute.LibView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text=""
lib:xattr="Custom attribute APPLIED!"
/>
</LinearLayout>
<resources>
<declare-styleable name="CustomAttrs">
<attr name="xattr" format="string" />
</declare-styleable>
</resources>
in values/attrs.xml

ListPopupWindow items not drawing correctly

I have a ListPopupWindow that is being populated from a custom adapter. The custom xml consists of 2 textviews and a LinearLayout. The custom adapter populates the textviews and instantiates a custom view that draws a thumbnail sized vector based graphic. The custom view is then added to the Linear Layout.
The problem is that the first row displayed always has the wrong graphic displayed. It is sometimes the graphic from another row, sometimes half of it is the correct graphic, half from the wrong row. I've also noticed that if the list has enough rows in it to scroll, the items at the bottom of the list have the same problem when scrolling.
Has anyone else experienced this?
Custom List Adapter:
public class PageSearchListAdapter extends SimpleCursorAdapter
{
int _height = 130;
int _width = 130;
Cursor c;
Context context;
// Constructor, here we store any parameters we need in class variables
//
public PageSearchListAdapter(Context context,
int layout,
Cursor c,
String[] from,
int[] to)
{
super(context, layout, c, from, to);
this.c = c;
this.context=context;
}
// Main view method - the views in query_list_item are populated here
//
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
// Make sure we have a view to work with
//
if(convertView == null)
convertView = View.inflate(context, R.layout.query_list_item, null);
View row = convertView;
// go to the correct row in the cursor
//
c.moveToPosition(position);
// Get the views that need populating
//
TextView PageNum = (TextView) convertView.findViewById(R.id.Page_Num);
LinearLayout pic = (LinearLayout) convertView.findViewById(R.id.Page_Canvas);
TextView Date = (TextView) convertView.findViewById(R.id.Page_Date);
// Set the values
//
PageNum.setText(c.getString(c.getColumnIndex("PageNum")));
Date.setText(c.getString(c.getColumnIndex("Timestamp")));
List<Point> Vectors = new ArrayList<Point>();
byte[] asBytes = c.getBlob(c.getColumnIndex("Vectors"));
..
.. removed code to Convert Blob to array of 'Vector' records ...
..
// Draw the page
//
NotePadPage npPage = new NotePadPage(context, Vectors);
npPage.setLayoutParams(new LayoutParams(_width, _height));
pic.addView(npPage);
return(row);
}
}
NotePadPage class:
// Adapter to display a custom list item in a list view
//
public class NotePadPage extends View
{
int _height = 130;
int _width = 130;
Bitmap _bitmap;
Canvas _canvas;
Paint _paint;
List<Point> _Vectors = new ArrayList<Point>();
public NotePadPage(Context context, List<Point> Vectors)
{
super(context);
_Vectors = Vectors;
_paint = new Paint();
_paint.setColor(Color.WHITE);
_paint.setStyle(Paint.Style.STROKE);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
_height = View.MeasureSpec.getSize(heightMeasureSpec);
_width = View.MeasureSpec.getSize(widthMeasureSpec);
setMeasuredDimension(_width, _height);
_bitmap = Bitmap.createBitmap(_width, _height, Bitmap.Config.ARGB_8888);
_canvas = new Canvas(_bitmap);
}
#Override
protected void onDraw(Canvas canvas)
{
float yRatio = 1092/ _height;
float xRatio = 800 / _width;
Point lastPoint = null;
for (Point point : _Vectors)
{
switch (point.Type)
{
case Start:
{
lastPoint = point;
break;
}
case Midpoint:
case End:
{
canvas.drawLine(lastPoint.x / xRatio, lastPoint.y/ yRatio, point.x / xRatio, point.y/ yRatio, _paint);
lastPoint = point;
}
}
}
}
}
Referenced Classes:
public class Point
{
public float x, y;
public PointType Type;
}
public enum PointType
{
Start,
Midpoint ,
End;
}
XML for List row:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_vertical|center_horizontal" >
<TextView
android:id="#+id/Page_Num"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical|center_horizontal"
android:layout_marginRight="5dp"
android:text="1"
android:textAppearance="?android:attr/textAppearanceMedium" />
<LinearLayout
android:id="#+id/Page_Canvas"
android:layout_width="130dip"
android:layout_height="130dip"
android:gravity="center_vertical|center_horizontal"
android:layout_toRightOf="#id/Page_Num">
</LinearLayout>
<TextView
android:id="#+id/Page_Date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical|center_horizontal"
android:layout_marginLeft="5dp"
android:text="Medium Text"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_toRightOf="#id/Page_Canvas">
</TextView>
</RelativeLayout>

Categories

Resources