3:17AM and I could drown in my own dam of sleep, but rather I chose to stay up and for the very first time in my life, actually work on UI of an android app. I am not a UI guy, like at all. I have never coded any good UI and never created something aesthetic and eye pleasing. And here I am doing it for the first time. Reason, you might ask. Well, I have a deadline to meet which is a big deal because it's the private beta of Grid and I can't afford to lose on that, because there are going to be actual users and living people involved(devs are pretty much dead inside). So the app I am working on is like twitch for audio streaming and involves Grid to run offline. And this post is about little thing(s) regarding basics of UI that I learned and might help someone.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//This class lets you add spaces between characters in a TextView and fails terribly at being hard to use. | |
//Source:http://stackoverflow.com/a/23216171/2730066 | |
package com.example.iostreamer.zealui; | |
import android.content.Context; | |
import android.text.Spannable; | |
import android.text.SpannableString; | |
import android.text.style.ScaleXSpan; | |
import android.util.AttributeSet; | |
import android.widget.TextView; | |
public class LetterSpacingTextView extends TextView { | |
private float letterSpacing = LetterSpacing.BIGGEST; | |
private CharSequence originalText = ""; | |
public LetterSpacingTextView(Context context) { | |
super(context); | |
} | |
public LetterSpacingTextView(Context context, AttributeSet attrs){ | |
super(context, attrs); | |
originalText = super.getText(); | |
applyLetterSpacing(); | |
this.invalidate(); | |
} | |
public LetterSpacingTextView(Context context, AttributeSet attrs, int defStyle){ | |
super(context, attrs, defStyle); | |
} | |
public float getLetterSpacing() { | |
return letterSpacing; | |
} | |
public void setLetterSpacing(float letterSpacing) { | |
this.letterSpacing = letterSpacing; | |
applyLetterSpacing(); | |
} | |
@Override | |
public void setText(CharSequence text, BufferType type) { | |
originalText = text; | |
applyLetterSpacing(); | |
} | |
@Override | |
public CharSequence getText() { | |
return originalText; | |
} | |
private void applyLetterSpacing() { | |
if (this == null || this.originalText == null) return; | |
StringBuilder builder = new StringBuilder(); | |
for(int i = 0; i < originalText.length(); i++) { | |
String c = ""+ originalText.charAt(i); | |
builder.append(c.toLowerCase()); | |
if(i+1 < originalText.length()) { | |
builder.append("\u00A0"); | |
} | |
} | |
SpannableString finalText = new SpannableString(builder.toString()); | |
if(builder.toString().length() > 1) { | |
for(int i = 1; i < builder.toString().length(); i+=2) { | |
finalText.setSpan(new ScaleXSpan((letterSpacing+1)/10), i, i+1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); | |
} | |
} | |
super.setText(finalText, BufferType.SPANNABLE); | |
} | |
public class LetterSpacing { | |
public final static float NORMAL = 0; | |
public final static float NORMALBIG = (float)0.025; | |
public final static float BIG = (float)0.05; | |
public final static float BIGGEST = (float)0.2; | |
} | |
} | |
//If you are translating some view and want to move the view too then you should use ViewPropertyAnimator, again very easy to use. And yea | |
//it lets you chain animations | |
View.animate().translationX(0); | |
View.animate().x(256); | |
View.animate().alpha(0.3f).translateY(230); //easy chaining | |
//If you want to push commands to a ScrollView like fullScroll, don't use them directly and wonder why it doesn't work. Use | |
//it like this: | |
final ScrollView dump = (ScrollView) itemView.findViewById(R.id.dump); | |
dump.post(new Runnable() { | |
@Override | |
public void run() { | |
dump.fullScroll(View.FOCUS_DOWN); | |
} | |
}); | |
//In a pager adapter class, lets say you have 2 views, one has a scrollview and other has an imageview and lets say that the page | |
//with image view is in focus and you add a view to scrollview in runtime. This is going to lead to an exception(NPE) if you inflated | |
//view for each page at runtime and initialized that View object with null(I know I am rookie). To prevent this, don't add a null view to the container | |
//in instantiateView(PS: the magic is in the null check in statement above return). Here's an example: | |
@Override | |
public Object instantiateItem(ViewGroup container, int position) { | |
LayoutInflater lf = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); | |
View itemView = null; | |
switch (position) { | |
case 0: | |
itemView = lf.inflate(R.layout.view_pager_main, container, | |
false); | |
LinearLayout stream = (LinearLayout) itemView.findViewById(R.id.stream); | |
for (int i = 0; i < display.size(); i++) { | |
lf.inflate(R.layout.card, stream); | |
} | |
for (int j = 0; j < stream.getChildCount(); j++) { | |
View card = stream.getChildAt(j); | |
ImageView cover = (ImageView) card.findViewById(R.id.coverArt); | |
Bitmap amethyst = BitmapFactory.decodeResource(context.getResources(), display.get(j)); | |
Bitmap amethystgrey = toGrayscale(amethyst); | |
cover.setImageBitmap(amethystgrey); | |
LetterSpacingTextView listen = (LetterSpacingTextView) card.findViewById(R.id.listen); | |
listen.setLetterSpacing(15); | |
} | |
final ScrollView dump = (ScrollView) itemView.findViewById(R.id.dump); | |
dump.post(new Runnable() { | |
@Override | |
public void run() { | |
dump.fullScroll(View.FOCUS_DOWN); | |
} | |
}); | |
break; | |
case 1: | |
itemView = lf.inflate(R.layout.view_pager_right, container, | |
false); | |
ImageView iv = (ImageView) itemView.findViewById(R.id.imageView); | |
Bitmap all = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_launcher); | |
Bitmap grey = toGrayscale(all); | |
iv.setImageBitmap(grey); | |
break; | |
} | |
if (itemView != null) | |
((ViewPager) container).addView(itemView); | |
return itemView; | |
} | |
//A very simple function to turn a bitmap to a greyscale bitmap | |
//Source:http://stackoverflow.com/a/3391061/2730066 | |
public Bitmap toGrayscale(Bitmap bmpOriginal) { | |
int width, height; | |
height = bmpOriginal.getHeight(); | |
width = bmpOriginal.getWidth(); | |
Bitmap bmpGrayscale = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); | |
Canvas c = new Canvas(bmpGrayscale); | |
Paint paint = new Paint(); | |
ColorMatrix cm = new ColorMatrix(); | |
cm.setSaturation(0); | |
ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm); | |
paint.setColorFilter(f); | |
c.drawBitmap(bmpOriginal, 0, 0, paint); | |
return bmpGrayscale; | |
} |
No comments:
Post a Comment