Android discreet seekbar not showing tickmarks for max values below 2 - android

Setting the discreet seekbar attributes to android:max="1" does not show the tickmark on both ends of the seekbar even though its seekable.
But setting android:max="2" shows the three tickmarks.
How to make the tickmark appear for max value 0 and 1?

If you look at the source code for AbsSeekBar, you'll find this method:
/**
* #hide
*/
protected void drawTickMarks(Canvas canvas) {
if (mTickMark != null) {
final int count = getMax() - getMin();
if (count > 1) {
final int w = mTickMark.getIntrinsicWidth();
final int h = mTickMark.getIntrinsicHeight();
final int halfW = w >= 0 ? w / 2 : 1;
final int halfH = h >= 0 ? h / 2 : 1;
mTickMark.setBounds(-halfW, -halfH, halfW, halfH);
final float spacing = (getWidth() - mPaddingLeft - mPaddingRight) / (float) count;
final int saveCount = canvas.save();
canvas.translate(mPaddingLeft, getHeight() / 2);
for (int i = 0; i <= count; i++) {
mTickMark.draw(canvas);
canvas.translate(spacing, 0);
}
canvas.restoreToCount(saveCount);
}
}
}
The important part is the line if (count > 1); for whatever reason, the Google team decided that tick marks shouldn't be shown if there were only two selectable values.
There's no great way to work around this.
You'd have to override this entire method, but it's annotated with #hide which means that the outside world shouldn't know about it. This won't actually stop you from doing it, but it means that you always have to worry about it breaking whenever Android is updated.
The other problem is that this method uses a lot of fields that you won't have access to. You can use getters to work around that but it's a little gnarly.
Put together, you can make this class:
public class MySeekBar extends SeekBar {
public MySeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
protected void drawTickMarks(Canvas canvas) {
if (getTickMark() != null) {
final int count = getMax() - getMin();
// this is the important part: you change this to 0 from 1
if (count > 0) {
final int w = getTickMark().getIntrinsicWidth();
final int h = getTickMark().getIntrinsicHeight();
final int halfW = w >= 0 ? w / 2 : 1;
final int halfH = h >= 0 ? h / 2 : 1;
getTickMark().setBounds(-halfW, -halfH, halfW, halfH);
final float spacing = (getWidth() - getPaddingLeft() - getPaddingRight()) / (float) count;
final int saveCount = canvas.save();
canvas.translate(getPaddingLeft(), getHeight() / 2);
for (int i = 0; i <= count; i++) {
getTickMark().draw(canvas);
canvas.translate(spacing, 0);
}
canvas.restoreToCount(saveCount);
}
}
}
}

Related

Matrix.postScale produces pixelated image

I'm using a library that extends the Gallery view.
However, I'm having a problem with the unselected items being pixelated. Digging into the code, I suspect that this is the function that does the image manipulations based on the input.
My question is that, is there a way to ensure that when the code calls postScale on the image matrix, my images wont get pixelated?
Hoping that someone can point me in the right direction.
The value of unselectedScale = 0.2f
#Override
protected boolean getChildStaticTransformation(View child, Transformation t) {
// We can cast here because FancyCoverFlowAdapter only creates wrappers.
FancyCoverFlowItemWrapper item = (FancyCoverFlowItemWrapper) child;
// Since Jelly Bean childs won't get invalidated automatically, needs to be added for the smooth coverflow animation
if (android.os.Build.VERSION.SDK_INT >= 16) {
item.invalidate();
}
final int coverFlowWidth = this.getWidth();
final int coverFlowCenter = coverFlowWidth / 2;
final int childWidth = item.getWidth();
final int childHeight = item.getHeight();
final int childCenter = item.getLeft() + childWidth / 2;
// Use coverflow width when its defined as automatic.
final int actionDistance = (this.actionDistance == ACTION_DISTANCE_AUTO) ? (int) ((coverFlowWidth + childWidth) / 2.0f) : this.actionDistance;
// Calculate the abstract amount for all effects.
final float effectsAmount = Math.min(1.0f, Math.max(-1.0f, (1.0f / actionDistance) * (childCenter - coverFlowCenter)));
// Clear previous transformations and set transformation type (matrix + alpha).
t.clear();
t.setTransformationType(Transformation.TYPE_BOTH);
// Alpha
if (this.unselectedAlpha != 1) {
final float alphaAmount = (this.unselectedAlpha - 1) * Math.abs(effectsAmount) + 1;
t.setAlpha(alphaAmount);
}
// Saturation
if (this.unselectedSaturation != 1) {
// Pass over saturation to the wrapper.
final float saturationAmount = (this.unselectedSaturation - 1) * Math.abs(effectsAmount) + 1;
item.setSaturation(saturationAmount);
}
final Matrix imageMatrix = t.getMatrix();
// Apply rotation.
if (this.maxRotation != 0) {
final int rotationAngle = (int) (-effectsAmount * this.maxRotation);
this.transformationCamera.save();
this.transformationCamera.rotateY(rotationAngle);
this.transformationCamera.getMatrix(imageMatrix);
this.transformationCamera.restore();
}
// Zoom.
if (this.unselectedScale != 1) {
final float zoomAmount = (this.unselectedScale - 1) * Math.abs(effectsAmount) + 1;
// Calculate the scale anchor (y anchor can be altered)
final float translateX = childWidth / 2.0f;
final float translateY = childHeight * this.scaleDownGravity;
imageMatrix.preTranslate(-translateX, -translateY);
imageMatrix.postScale(zoomAmount, zoomAmount);
imageMatrix.postTranslate(translateX, translateY);
}
return true;
}

RecyclerView with Alphabet Index

I am attempting to build a RecyclerView list, with an alphabetical indexer down the right hand side. I have the list displaying perfectly and am able to scroll.
I am wondering if someone could give me some guidance in building the alphabetical indexer. I believe I need to iterate through my list and determine the letters used (I can do this). Then build the list of letters (for the indexer) and then use scrollToPosition(int position) to move to the right item in the list when a letter is selected.
The main issue that I can not work out is not building another list, it is rather the communication between the two lists. Any help would be greatly appreciated.
I believe this is the information you have been looking for as I have
You can check these libraries here:
danoz73/RecyclerViewFastScroller
as far as I remember he is the one who actually made the first alphabet indexer in recyclerview. I never made it work in my app because there was lots of problem customizing it.
AndroidDeveloperLB/LollipopContactsRecyclerViewFastScroller
the readme said hes got lots of optimizations made, So i think this is the best one i could use, but i havent made it work in my app for now.
code-computerlove/FastScrollRecyclerView
I think this is the design you want right? So either of these libs you used it should suffice your needs.
hope it helps
If anyone would like to use the FastScrollRecyclerView mentioned above in Xamarin, I just ported the main classes (you'll still need to create the xml resource and prep your index etc etc - see the original repo for a guide, it's pretty simple).
I included the tweaks suggested in the issues page to improve the ScrollToPosition and index height (which also makes it work in landscape) and also switched out the Java Handler class for an async Task.Delay. It may still have issues, it's a straight port otherwise. Seems to be ok though. Hopefully I have saved someone some time :)
public interface IFastScrollRecyclerViewAdapter
{
Dictionary<string, int> GetMapIndex();
}
//
public class FastScrollRecyclerView : RecyclerView
{
public const int INDWIDTH = 25;
public const int INDHEIGHT = 18;
public float ScaledWidth { get; set; }
public float ScaledHeight { get; set; }
public string[] Sections { get; set; }
public float Sx { get; set; }
public float Sy { get; set; }
public string Section { get; set; }
public bool ShowLetter { get; set; }
private ListHandler _listHandler;
private bool _setupThings = false;
private Context _context;
public FastScrollRecyclerView(Context context) : base(context)
{
_context = context;
}
public FastScrollRecyclerView(Context context, IAttributeSet attrs) : base(context, attrs)
{
_context = context;
}
public FastScrollRecyclerView(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle)
{
_context = context;
}
public override void OnDraw(Canvas c)
{
if (!_setupThings && GetAdapter() != null)
SetupThings();
base.OnDraw(c);
}
private void SetupThings()
{
//create az text data
var sectionSet = ((IFastScrollRecyclerViewAdapter)GetAdapter()).GetMapIndex().Keys;
var listSection = new List<string>(sectionSet);
listSection.Sort();
Sections = new string[listSection.Count];
int i = 0;
foreach (var s in listSection)
{
Sections[i++] = s;
}
ScaledWidth = INDWIDTH * _context.Resources.DisplayMetrics.Density;
var divisor = sectionSet.Count == 0 ? 1 : sectionSet.Count;
ScaledHeight = Height / divisor ;// INDHEIGHT * _context.Resources.DisplayMetrics.Density;
Sx = Width - PaddingRight - (float)(1.2 * ScaledWidth);
Sy = (float)((Height - (ScaledHeight * Sections.Length)) / 2.0);
_setupThings = true;
}
public override bool OnTouchEvent(MotionEvent motionEvent)
{
if (_setupThings)
{
var adapter = GetAdapter() as IFastScrollRecyclerViewAdapter;
var x = motionEvent.GetX();
var y = motionEvent.GetY();
switch (motionEvent.Action)
{
case MotionEventActions.Down:
{
if (x < Sx - ScaledWidth || y < Sy || y > (Sy + ScaledHeight * Sections.Length))
{
return base.OnTouchEvent(motionEvent);
}
else
{
//// We touched the index bar
float yy = y - PaddingTop - PaddingBottom - Sy;
int currentPosition = (int)Math.Floor(yy / ScaledHeight);
if (currentPosition < 0) currentPosition = 0;
if (currentPosition >= Sections.Length) currentPosition = Sections.Length - 1;
Section = Sections[currentPosition];
ShowLetter = true;
int positionInData = 0;
if (adapter.GetMapIndex().ContainsKey(Section.ToUpper()))
{
positionInData = adapter.GetMapIndex()[Section.ToUpper()];
}
(GetLayoutManager() as LinearLayoutManager).ScrollToPositionWithOffset(positionInData, 20);
Invalidate();
}
break;
}
case MotionEventActions.Move:
{
if (!ShowLetter && (x < Sx - ScaledWidth || y < Sy || y > (Sy + ScaledHeight * Sections.Length)))
{
return base.OnTouchEvent(motionEvent);
}
else
{
float yy = y - Sy;
int currentPosition = (int)Math.Floor(yy / ScaledHeight);
if (currentPosition < 0) currentPosition = 0;
if (currentPosition >= Sections.Length) currentPosition = Sections.Length - 1;
Section = Sections[currentPosition];
ShowLetter = true;
int positionInData = 0;
if (adapter.GetMapIndex().ContainsKey(Section.ToUpper()))
positionInData = adapter.GetMapIndex()[Section.ToUpper()];
(GetLayoutManager() as LinearLayoutManager).ScrollToPositionWithOffset(positionInData, 20);
Invalidate();
}
break;
}
case MotionEventActions.Up:
{
_listHandler = new ListHandler(this);
_listHandler.DelayClear();
if (x < Sx - ScaledWidth || y < Sy || y > (Sy + ScaledHeight * Sections.Length))
{
return base.OnTouchEvent(motionEvent);
}
else
{
return true;
}
}
}
}
return true;
}
private class ListHandler
{
FastScrollRecyclerView _parent;
public ListHandler (FastScrollRecyclerView parent)
{
_parent = parent;
}
public async void DelayClear()
{
await Task.Delay(100);
_parent.ShowLetter = false;
_parent.Invalidate();
}
}
}
//
public class FastScrollRecyclerViewItemDecoration : ItemDecoration
{
private Context _context;
public FastScrollRecyclerViewItemDecoration(Context context)
{
_context = context;
}
public override void OnDrawOver(Canvas canvas, RecyclerView parent, State state)
{
base.OnDrawOver(canvas, parent, state);
float scaledWidth = ((FastScrollRecyclerView)parent).ScaledWidth;
float sx = ((FastScrollRecyclerView)parent).Sx;
float scaledHeight = ((FastScrollRecyclerView)parent).ScaledHeight;
float sy = ((FastScrollRecyclerView)parent).Sy;
string[] sections = ((FastScrollRecyclerView)parent).Sections;
string section = ((FastScrollRecyclerView)parent).Section;
bool showLetter = ((FastScrollRecyclerView)parent).ShowLetter;
// We draw the letter in the middle
if (showLetter & section != null && !section.Equals(""))
{
//overlay everything when displaying selected index Letter in the middle
Paint overlayDark = new Paint();
overlayDark.Color = Color.Black;
overlayDark.Alpha = 100;
canvas.DrawRect(0, 0, parent.Width, parent.Height, overlayDark);
float middleTextSize = _context.Resources.GetDimension(Resource.Dimension.fast_scroll_overlay_text_size );
Paint middleLetter = new Paint();
middleLetter.Color = new Color(ContextCompat.GetColor(_context, Resource.Color.primary));
middleLetter.TextSize = middleTextSize;
middleLetter.AntiAlias = true;
middleLetter.FakeBoldText = true;
middleLetter.SetStyle(Paint.Style.Fill);
int xPos = (canvas.Width - (int)middleTextSize) / 2;
int yPos = (int)((canvas.Height / 2) - ((middleLetter.Descent() + middleLetter.Ascent()) / 2));
canvas.DrawText(section.ToUpper(), xPos, yPos, middleLetter);
}
// // draw indez A-Z
Paint textPaint = new Paint();
textPaint.AntiAlias = true;
textPaint.SetStyle(Paint.Style.Fill);
for (int i = 0; i < sections.Length; i++)
{
if (showLetter & section != null && !section.Equals("") && section != null
&& sections[i].ToUpper().Equals(section.ToUpper()))
{
textPaint.Color = Color.White;
textPaint.Alpha = 255;
textPaint.FakeBoldText = true;
textPaint.TextSize = scaledWidth / 2;
canvas.DrawText(sections[i].ToUpper(),
sx + textPaint.TextSize / 2, sy + parent.PaddingTop
+ scaledHeight * (i + 1), textPaint);
textPaint.TextSize = scaledWidth;
canvas.DrawText("•",
sx - textPaint.TextSize / 3, sy + parent.PaddingTop
+ scaledHeight * (i + 1) + scaledHeight / 3, textPaint);
}
else
{
textPaint.Color = new Color(ContextCompat.GetColor(_context, Resource.Color.primary));
textPaint.Alpha = 200;
textPaint.FakeBoldText = false;
textPaint.TextSize = scaledWidth / 2;
canvas.DrawText(sections[i].ToUpper(),
sx + textPaint.TextSize / 2, sy + parent.PaddingTop
+ scaledHeight * (i + 1), textPaint);
}
}
}
}

Nine-patch content area not working

I'm trying to make a frame for TextView as a cloud. But the content area does not behave as expected. What am i doing wrong?
I have a suggestion that is not working properly because the content area less scale area. So sad. I remade it to handle 9-patch manually. Save pictures without .9.png. Get Bitmap. There are 9-line present. With getPixels calculated padding and set it on the TextView. After that calculating and set LayoutParams.width and LayoutParams.height. Looks a bit ugly, but it works quite quickly, and most importantly correctly.
private int startX=-1;
private int endX=-1;
private int contentW=-1;
private int contentH=-1;
Bitmap bmp=BitmapFactory.decodeResource(getResources(), mIconResId);
int[] pixels=new int[bmp.getWidth()*bmp.getHeight()];
bmp.getPixels(pixels, 0, bmp.getWidth(), 0, 0, bmp.getWidth(),bmp.getHeight());
for(int i=0;i<bmp.getWidth();i++){
if(startX==-1 && pixels[bmp.getWidth()*(bmp.getHeight()-1)+i]==Color.BLACK){
startX=i;
}
if(startX!=-1 && pixels[bmp.getWidth()*(bmp.getHeight()-1)+i]!=Color.BLACK){
endX=i;
break;
}
}
int startY=-1;
int endY=-1;
for(int i=0;i<bmp.getHeight();i++){
if(startY==-1 && pixels[bmp.getWidth()*(i+1)-1]==Color.BLACK){
startY=i;
}
if(startY!=-1 && pixels[bmp.getWidth()*(i+1)-1]!=Color.BLACK){
endY=i;
break;
}
}
setBackground(new BitmapDrawable(getResources(),Bitmap.createBitmap(bmp, 1, 1, bmp.getWidth()-2, bmp.getHeight()-2)));
contentW=endX-startX;
endX=bmp.getWidth()-endX;
contentH=endY-startY;
endY=bmp.getHeight()-endY;
new Handler().post(new Rannable(){
#Override
public void run() {
int w=textview.getWidth();
int h=textview.getHeight();
if(w>endX-startX){
float k=((float)w)/contentW;
startX=(int) (startX*k);
endX=(int) (endX*k);
}
if(h>endY-startY){
float k=((float)h)/contentH;
startY=(int) (startY*k);
endY=(int) (endY*k);
}
w+=startX+startX;
h+=startY+endY;
textview.setPadding(startX, startY, endX, endY);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(w,h);
textview.setLayoutParams(lp);
}
});
You set good values for right and bottom borders. You just have to set same values for left and top borders, left border = right border and top border = bottom border.
The result in draw9patch:
And here the 9-patch file:
For your information, your image is not really suitable for using with 9-patch format.
I extended/adapted #ahtartam code. I am not sure if it is the cleanest way but it works for me. If someone needs help, just contact me or ask in comments!
public void setTextLayout(int orgW, int orgH,int actW,int actH,int top,int left) {
int startX = -1;
int endX = -1;
int startY = -1;
int endY = -1;
int contentW;
int contentH;
Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.image);
int[] pixels = new int[orgW * orgH];
bmp.getPixels(pixels, 0, orgW, 0, 0, orgW, orgH);
for (int i = 0; i < orgW; i++) {
if (startX == -1 && pixels[orgW * (orgH - 1) + i] == Color.BLACK) {
startX = i;
}
if (startX != -1 && pixels[orgW * (orgH - 1) + i] != Color.BLACK) {
endX = i;
break;
}
}
for (int i = 0; i < orgH; i++) {
if (startY == -1 && pixels[orgW * (i + 1) - 1] == Color.BLACK) {
startY = i;
}
if (startY != -1 && pixels[orgW * (i + 1) - 1] != Color.BLACK) {
endY = i;
break;
}
}
m_marvin.setImageDrawable(new BitmapDrawable(getResources(), Bitmap.createBitmap(bmp, 1, 1, orgW - 2, orgH - 2)));
RelativeLayout.LayoutParams rp = (RelativeLayout.LayoutParams) m_marvin.getLayoutParams();
contentW=endX- startX;
contentH=endY-startY;
endX=orgW-endX;
endY=orgH-endY;
double scaleX = ((double)actW) / bmp.getWidth();
double scaleY = ((double)actH) / bmp.getHeight();
startX = (int) (startX * scaleX);
endX = (int) (endX * scaleX);
startY = (int) (startY * scaleY);
endY = (int) (endY * scaleY) ;
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams((int)(contentW*scaleX),(int)(contentH*scaleY));
layoutParams.setMargins(startX+rp.leftMargin+left, startY+rp.topMargin+top, endX+rp.rightMargin, endY+rp.bottomMargin);
layoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL,RelativeLayout.TRUE);
m_text.setLayoutParams(layoutParams);
m_text.bringToFront();
}
Instead TextView I use SizeAwareImageView from -> https://stackoverflow.com/a/15538856/1438596
In my case it looks like this->
public class SizeAwareImageView extends ImageView {
MainActivity m_mainActivity;
public SizeAwareImageView(Context context,AttributeSet attrss){
super(context,attrss);
m_mainActivity = (MainActivity)context;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if(m_mainActivity.getTextMeasured())return;
// Get image matrix values and place them in an array
float[] f = new float[9];
getImageMatrix().getValues(f);
// Extract the scale values using the constants (if aspect ratio maintained, scaleX == scaleY)
final float scaleX = f[Matrix.MSCALE_X];
final float scaleY = f[Matrix.MSCALE_Y];
// Get the drawable (could also get the bitmap behind the drawable and getWidth/getHeight)
final Drawable d = getDrawable();
final int origW = d.getIntrinsicWidth();
final int origH = d.getIntrinsicHeight();
// Calculate the actual dimensions
final int actW = Math.round(origW * scaleX);
final int actH = Math.round(origH * scaleY);
int top = (int) (imgViewH - actH)/2;
int left = (int) (imgViewW - actW)/2;
if(origW!=actW){
m_mainActivity.setTextMeasured(true);
m_mainActivity.setTextLayout(origW, origH, actW, actH,top,left);
}
}
}
You could use this tool for creating your nine-patch images.

Rotate YUV420/NV21 Image in android

In PreviewCall back of surface we are getting YUV420SP format in camera Preview but due to wrong rotation of that image I want to perform correct rotation of YUV image as I need to send it through network.so correct rotation need to be applied.
I found this link it does correct rotation but image loose the color.
http://www.wordsaretoys.com/2013/10/25/roll-that-camera-zombie-rotation-and-coversion-from-yv12-to-yuv420planar/
also checked Rotate an YUV byte array on Android but it does not show image properly.
I do have checked links on stckoverflow but none of them have satisfactory answer about correctly using the code in android environment.
do any one have idea how to correctly rotate NV21 Image bytes[] with retaining its color information correctly.
If you just want to rotate NV21, following code will be helpful. (I modified the code from here)
public static void rotateNV21(byte[] input, byte[] output, int width, int height, int rotation) {
boolean swap = (rotation == 90 || rotation == 270);
boolean yflip = (rotation == 90 || rotation == 180);
boolean xflip = (rotation == 270 || rotation == 180);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int xo = x, yo = y;
int w = width, h = height;
int xi = xo, yi = yo;
if (swap) {
xi = w * yo / h;
yi = h * xo / w;
}
if (yflip) {
yi = h - yi - 1;
}
if (xflip) {
xi = w - xi - 1;
}
output[w * yo + xo] = input[w * yi + xi];
int fs = w * h;
int qs = (fs >> 2);
xi = (xi >> 1);
yi = (yi >> 1);
xo = (xo >> 1);
yo = (yo >> 1);
w = (w >> 1);
h = (h >> 1);
// adjust for interleave here
int ui = fs + (w * yi + xi) * 2;
int uo = fs + (w * yo + xo) * 2;
// and here
int vi = ui + 1;
int vo = uo + 1;
output[uo] = input[ui];
output[vo] = input[vi];
}
}
}
Eddy Yong`s code works well with 180 degrees only, but gained some upvotes in here and a lot of copy-pastes in other SO topics and other similar (e.g. github discussions), so I've decided to leave here proper answer for future researchers, and rotation method, which works for 90, 180 and 270 deegres
public static byte[] rotateNV21(final byte[] yuv,
final int width,
final int height,
final int rotation)
{
if (rotation == 0) return yuv;
if (rotation % 90 != 0 || rotation < 0 || rotation > 270) {
throw new IllegalArgumentException("0 <= rotation < 360, rotation % 90 == 0");
}
final byte[] output = new byte[yuv.length];
final int frameSize = width * height;
final boolean swap = rotation % 180 != 0;
final boolean xflip = rotation % 270 != 0;
final boolean yflip = rotation >= 180;
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++) {
final int yIn = j * width + i;
final int uIn = frameSize + (j >> 1) * width + (i & ~1);
final int vIn = uIn + 1;
final int wOut = swap ? height : width;
final int hOut = swap ? width : height;
final int iSwapped = swap ? j : i;
final int jSwapped = swap ? i : j;
final int iOut = xflip ? wOut - iSwapped - 1 : iSwapped;
final int jOut = yflip ? hOut - jSwapped - 1 : jSwapped;
final int yOut = jOut * wOut + iOut;
final int uOut = frameSize + (jOut >> 1) * wOut + (iOut & ~1);
final int vOut = uOut + 1;
output[yOut] = (byte)(0xff & yuv[yIn]);
output[uOut] = (byte)(0xff & yuv[uIn]);
output[vOut] = (byte)(0xff & yuv[vIn]);
}
}
return output;
}
originally posted by jake in this SO topic

ToolBar on MonoDroid

Do you know any examples, screencasts that document how to develop with ToolBar on "mono for android" ?
I would like to develop a tiny toolbar and I'm searching for the proper way to code it?
Here is Dashboard, and if you or somebody else will need ActionBar here is a link!
public class DashboardLayout : ViewGroup
{
private const int UNEVEN_GRID_PENALTY_MULTIPLIER = 10;
private int _mMaxChildWidth;
private int _mMaxChildHeight;
public DashboardLayout(IntPtr doNotUse, JniHandleOwnership transfer) : base(doNotUse, transfer)
{
}
public DashboardLayout(Context context)
: base(context)
{
}
public DashboardLayout(Context context, IAttributeSet attrs)
: base(context, attrs)
{
}
public DashboardLayout(Context context, IAttributeSet attrs, int defStyle)
: base(context, attrs, defStyle)
{
}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
_mMaxChildWidth = 0;
_mMaxChildHeight = 0;
// Measure once to find the maximum child size.
var childWidthMeasureSpec = MeasureSpec.MakeMeasureSpec(
MeasureSpec.GetSize(widthMeasureSpec), MeasureSpecMode.AtMost);
var childHeightMeasureSpec = MeasureSpec.MakeMeasureSpec(
MeasureSpec.GetSize(widthMeasureSpec), MeasureSpecMode.AtMost);
var count = ChildCount;
for (var i = 0; i < count; i++)
{
var child = GetChildAt(i);
if (child.Visibility == ViewStates.Gone)
{
continue;
}
child.Measure(childWidthMeasureSpec, childHeightMeasureSpec);
_mMaxChildWidth = Math.Max(_mMaxChildWidth, child.MeasuredWidth);
_mMaxChildHeight = Math.Max(_mMaxChildHeight, child.MeasuredHeight);
}
// Measure again for each child to be exactly the same size.
childWidthMeasureSpec = MeasureSpec.MakeMeasureSpec(
_mMaxChildWidth, MeasureSpecMode.Exactly);
childHeightMeasureSpec = MeasureSpec.MakeMeasureSpec(
_mMaxChildHeight, MeasureSpecMode.Exactly);
for (int i = 0; i < count; i++)
{
var child = GetChildAt(i);
if (child.Visibility == ViewStates.Gone)
{
continue;
}
child.Measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
SetMeasuredDimension(
ResolveSize(_mMaxChildWidth, widthMeasureSpec),
ResolveSize(_mMaxChildHeight, heightMeasureSpec));
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
var width = r - l;
var height = b - t;
var count = ChildCount;
// Calculate the number of visible children.
var visibleCount = 0;
for (var i = 0; i < count; i++)
{
var child = GetChildAt(i);
if (child.Visibility == ViewStates.Gone)
{
continue;
}
++visibleCount;
}
if (visibleCount == 0)
{
return;
}
// Calculate what number of rows and columns will optimize for even horizontal and
// vertical whitespace between items. Start with a 1 x N grid, then try 2 x N, and so on.
var bestSpaceDifference = int.MaxValue;
// Horizontal and vertical space between items
int hSpace;
int vSpace;
var cols = 1;
int rows;
while (true)
{
rows = (visibleCount - 1) / cols + 1;
hSpace = ((width - _mMaxChildWidth * cols) / (cols + 1));
vSpace = ((height - _mMaxChildHeight * rows) / (rows + 1));
var spaceDifference = Math.Abs(vSpace - hSpace);
if (rows * cols != visibleCount)
{
spaceDifference *= UNEVEN_GRID_PENALTY_MULTIPLIER;
}
if (spaceDifference < bestSpaceDifference)
{
// Found a better whitespace squareness/ratio
bestSpaceDifference = spaceDifference;
// If we found a better whitespace squareness and there's only 1 row, this is
// the best we can do.
if (rows == 1)
{
break;
}
}
else
{
// This is a worse whitespace ratio, use the previous value of cols and exit.
--cols;
rows = (visibleCount - 1) / cols + 1;
hSpace = ((width - _mMaxChildWidth * cols) / (cols + 1));
vSpace = ((height - _mMaxChildHeight * rows) / (rows + 1));
break;
}
++cols;
}
// Lay out children based on calculated best-fit number of rows and cols.
// If we chose a layout that has negative horizontal or vertical space, force it to zero.
hSpace = Math.Max(0, hSpace);
vSpace = Math.Max(0, vSpace);
// Re-use width/height variables to be child width/height.
width = (width - hSpace * (cols + 1)) / cols;
height = (height - vSpace * (rows + 1)) / rows;
var visibleIndex = 0;
for (var i = 0; i < count; i++)
{
var child = GetChildAt(i);
if (child.Visibility == ViewStates.Gone)
{
continue;
}
var row = visibleIndex / cols;
var col = visibleIndex % cols;
var left = hSpace * (col + 1) + width * col;
var top = vSpace * (row + 1) + height * row;
child.Layout(left, top,
(hSpace == 0 && col == cols - 1) ? r : (left + width),
(vSpace == 0 && row == rows - 1) ? b : (top + height));
++visibleIndex;
}
}
}

Categories

Resources