add gmail-like round drawable for compact mode in place of checkbox

This commit is contained in:
Andrew Dolgov 2014-11-16 13:13:39 +04:00
parent 51d4bef187
commit e5ab4e5968
12 changed files with 542 additions and 30 deletions

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.fox.ttrss"
android:versionCode="283"
android:versionName="1.75" >
android:versionCode="284"
android:versionName="1.76" >
<uses-sdk
android:minSdkVersion="10"

View File

@ -0,0 +1,316 @@
package com.amulyakhare.textdrawable;
import android.graphics.*;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.graphics.drawable.shapes.RectShape;
import android.graphics.drawable.shapes.RoundRectShape;
/**
* @author amulya
* @datetime 14 Oct 2014, 3:53 PM
*/
public class TextDrawable extends ShapeDrawable {
private final Paint textPaint;
private final Paint borderPaint;
private static final float SHADE_FACTOR = 0.9f;
private final String text;
private final int color;
private final RectShape shape;
private final int height;
private final int width;
private final int fontSize;
private final float radius;
private final int borderThickness;
private TextDrawable(Builder builder) {
super(builder.shape);
// shape properties
shape = builder.shape;
height = builder.height;
width = builder.width;
radius = builder.radius;
// text and color
text = builder.toUpperCase ? builder.text.toUpperCase() : builder.text;
color = builder.color;
// text paint settings
fontSize = builder.fontSize;
textPaint = new Paint();
textPaint.setColor(builder.textColor);
textPaint.setAntiAlias(true);
textPaint.setFakeBoldText(builder.isBold);
textPaint.setStyle(Paint.Style.FILL);
textPaint.setTypeface(builder.font);
textPaint.setTextAlign(Paint.Align.CENTER);
textPaint.setStrokeWidth(builder.borderThickness);
// border paint settings
borderThickness = builder.borderThickness;
borderPaint = new Paint();
borderPaint.setColor(getDarkerShade(color));
borderPaint.setStyle(Paint.Style.STROKE);
borderPaint.setStrokeWidth(borderThickness);
// drawable paint color
Paint paint = getPaint();
paint.setColor(color);
}
private int getDarkerShade(int color) {
return Color.rgb((int)(SHADE_FACTOR * Color.red(color)),
(int)(SHADE_FACTOR * Color.green(color)),
(int)(SHADE_FACTOR * Color.blue(color)));
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
Rect r = getBounds();
// draw border
if (borderThickness > 0) {
drawBorder(canvas);
}
int count = canvas.save();
canvas.translate(r.left, r.top);
// draw text
int width = this.width < 0 ? r.width() : this.width;
int height = this.height < 0 ? r.height() : this.height;
int fontSize = this.fontSize < 0 ? (Math.min(width, height) / 2) : this.fontSize;
textPaint.setTextSize(fontSize);
canvas.drawText(text, width / 2, height / 2 - ((textPaint.descent() + textPaint.ascent()) / 2), textPaint);
canvas.restoreToCount(count);
}
private void drawBorder(Canvas canvas) {
RectF rect = new RectF(getBounds());
rect.inset(borderThickness/2, borderThickness/2);
if (shape instanceof OvalShape) {
canvas.drawOval(rect, borderPaint);
}
else if (shape instanceof RoundRectShape) {
canvas.drawRoundRect(rect, radius, radius, borderPaint);
}
else {
canvas.drawRect(rect, borderPaint);
}
}
@Override
public void setAlpha(int alpha) {
textPaint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter cf) {
textPaint.setColorFilter(cf);
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
@Override
public int getIntrinsicWidth() {
return width;
}
@Override
public int getIntrinsicHeight() {
return height;
}
public static IShapeBuilder builder() {
return new Builder();
}
public static class Builder implements IConfigBuilder, IShapeBuilder, IBuilder {
private String text;
private int color;
private int borderThickness;
private int width;
private int height;
private Typeface font;
private RectShape shape;
public int textColor;
private int fontSize;
private boolean isBold;
private boolean toUpperCase;
public float radius;
private Builder() {
text = "";
color = Color.GRAY;
textColor = Color.WHITE;
borderThickness = 0;
width = -1;
height = -1;
shape = new RectShape();
font = Typeface.create("sans-serif-light", Typeface.NORMAL);
fontSize = -1;
isBold = false;
toUpperCase = false;
}
public IConfigBuilder width(int width) {
this.width = width;
return this;
}
public IConfigBuilder height(int height) {
this.height = height;
return this;
}
public IConfigBuilder textColor(int color) {
this.textColor = color;
return this;
}
public IConfigBuilder withBorder(int thickness) {
this.borderThickness = thickness;
return this;
}
public IConfigBuilder useFont(Typeface font) {
this.font = font;
return this;
}
public IConfigBuilder fontSize(int size) {
this.fontSize = size;
return this;
}
public IConfigBuilder bold() {
this.isBold = true;
return this;
}
public IConfigBuilder toUpperCase() {
this.toUpperCase = true;
return this;
}
@Override
public IConfigBuilder beginConfig() {
return this;
}
@Override
public IShapeBuilder endConfig() {
return this;
}
@Override
public IBuilder rect() {
this.shape = new RectShape();
return this;
}
@Override
public IBuilder round() {
this.shape = new OvalShape();
return this;
}
@Override
public IBuilder roundRect(int radius) {
this.radius = radius;
float[] radii = {radius, radius, radius, radius, radius, radius, radius, radius};
this.shape = new RoundRectShape(radii, null, null);
return this;
}
@Override
public TextDrawable buildRect(String text, int color) {
rect();
return build(text, color);
}
@Override
public TextDrawable buildRoundRect(String text, int color, int radius) {
roundRect(radius);
return build(text, color);
}
@Override
public TextDrawable buildRound(String text, int color) {
round();
return build(text, color);
}
@Override
public TextDrawable build(String text, int color) {
this.color = color;
this.text = text;
return new TextDrawable(this);
}
}
public interface IConfigBuilder {
public IConfigBuilder width(int width);
public IConfigBuilder height(int height);
public IConfigBuilder textColor(int color);
public IConfigBuilder withBorder(int thickness);
public IConfigBuilder useFont(Typeface font);
public IConfigBuilder fontSize(int size);
public IConfigBuilder bold();
public IConfigBuilder toUpperCase();
public IShapeBuilder endConfig();
}
public static interface IBuilder {
public TextDrawable build(String text, int color);
}
public static interface IShapeBuilder {
public IConfigBuilder beginConfig();
public IBuilder rect();
public IBuilder round();
public IBuilder roundRect(int radius);
public TextDrawable buildRect(String text, int color);
public TextDrawable buildRoundRect(String text, int color, int radius);
public TextDrawable buildRound(String text, int color);
}
}

View File

@ -0,0 +1,48 @@
package com.amulyakhare.textdrawable.util;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
/**
* @author amulya
* @datetime 14 Oct 2014, 5:20 PM
*/
public class ColorGenerator {
public static ColorGenerator DEFAULT;
static {
DEFAULT = create(Arrays.asList(
0xfff16364,
0xfff58559,
0xfff9a43e,
0xffe4c62e,
0xff67bf74,
0xff59a2be,
0xff2093cd,
0xffad62a7,
0xff805781
));
}
private final List<Integer> mColors;
private final Random mRandom;
public static ColorGenerator create(List<Integer> colorList) {
return new ColorGenerator(colorList);
}
private ColorGenerator(List<Integer> colorList) {
mColors = colorList;
mRandom = new Random(System.currentTimeMillis());
}
public int getRandomColor() {
return mColors.get(mRandom.nextInt(mColors.size()));
}
public int getColor(Object key) {
return mColors.get(Math.abs(key.hashCode()) % mColors.size());
}
}

View File

@ -41,6 +41,8 @@ import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.amulyakhare.textdrawable.TextDrawable;
import com.amulyakhare.textdrawable.util.ColorGenerator;
import com.google.gson.JsonElement;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
@ -603,6 +605,8 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
public ProgressBar flavorImageLoadingBar;
public View flavorImageArrow;
public View headlineFooter;
public ImageView textImage;
public ImageView textChecked;
}
private class ArticleListAdapter extends ArrayAdapter<Article> {
@ -619,6 +623,9 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
private final Integer[] origTitleColors = new Integer[VIEW_COUNT];
private final int titleHighScoreUnreadColor;
private ColorGenerator m_colorGenerator = ColorGenerator.DEFAULT;
private TextDrawable.IBuilder m_drawableBuilder = TextDrawable.builder().round();
public ArticleListAdapter(Context context, int textViewResourceId, ArrayList<Article> items) {
super(context, textViewResourceId, items);
this.items = items;
@ -650,13 +657,27 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
}
}
private void updateTextCheckedState(HeadlineViewHolder holder, Article item) {
String tmp = item.title.length() > 0 ? item.title.substring(0, 1) : "?";
if (m_selectedArticles.contains(item)) {
holder.textImage.setImageDrawable(m_drawableBuilder.build(" ", 0xff616161));
holder.textChecked.setVisibility(View.VISIBLE);
} else {
holder.textImage.setImageDrawable(m_drawableBuilder.build(tmp, m_colorGenerator.getColor(item.title)));
holder.textChecked.setVisibility(View.GONE);
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
final Article article = items.get(position);
HeadlineViewHolder holder;
final HeadlineViewHolder holder;
int headlineFontSize = Integer.parseInt(m_prefs.getString("headlines_font_size_sp", "13"));
int headlineSmallFontSize = Math.max(10, Math.min(18, headlineFontSize - 2));
@ -698,6 +719,8 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
holder.flavorImageLoadingBar = (ProgressBar) v.findViewById(R.id.flavorImageLoadingBar);
holder.flavorImageArrow = v.findViewById(R.id.flavorImageArrow);
holder.headlineFooter = v.findViewById(R.id.headline_footer);
holder.textImage = (ImageView) v.findViewById(R.id.text_image);
holder.textChecked = (ImageView) v.findViewById(R.id.text_checked);
v.setTag(holder);
@ -716,7 +739,31 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
}
});
}
if (holder.textImage != null) {
updateTextCheckedState(holder, article);
holder.textImage.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
Log.d(TAG, "textImage : onclicked");
if (!m_selectedArticles.contains(article)) {
m_selectedArticles.add(article);
} else {
m_selectedArticles.remove(article);
}
updateTextCheckedState(holder, article);
m_listener.onArticleListSelectionChange(m_selectedArticles);
Log.d(TAG, "num selected: " + m_selectedArticles.size());
}
});
}
if (holder.titleView != null) {
holder.titleView.setText(Html.fromHtml(article.title));

View File

@ -36,6 +36,9 @@ import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.amulyakhare.textdrawable.TextDrawable;
import com.amulyakhare.textdrawable.util.ColorGenerator;
import org.fox.ttrss.GlobalState;
import org.fox.ttrss.R;
import org.fox.ttrss.util.TypefaceCache;
@ -448,6 +451,8 @@ public class OfflineHeadlinesFragment extends Fragment implements OnItemClickLis
public ProgressBar flavorImageLoadingBar;
public View flavorImageArrow;
public View headlineFooter;
public ImageView textImage;
public ImageView textChecked;
}
private class ArticleListAdapter extends SimpleCursorAdapter {
@ -461,6 +466,9 @@ public class OfflineHeadlinesFragment extends Fragment implements OnItemClickLis
private final Integer[] origTitleColors = new Integer[VIEW_COUNT];
private final int titleHighScoreUnreadColor;
private ColorGenerator m_colorGenerator = ColorGenerator.DEFAULT;
private TextDrawable.IBuilder m_drawableBuilder = TextDrawable.builder().round();
public ArticleListAdapter(Context context, int layout, Cursor c,
String[] from, int[] to, int flags) {
@ -493,14 +501,32 @@ public class OfflineHeadlinesFragment extends Fragment implements OnItemClickLis
}
}
private void updateTextCheckedState(HeadlineViewHolder holder, Cursor item) {
String title = item.getString(item.getColumnIndex("title"));
String tmp = title.length() > 0 ? title.substring(0, 1) : "?";
boolean isChecked = item.getInt(item.getColumnIndex("selected")) == 1;
if (isChecked) {
holder.textImage.setImageDrawable(m_drawableBuilder.build(" ", 0xff616161));
holder.textChecked.setVisibility(View.VISIBLE);
} else {
holder.textImage.setImageDrawable(m_drawableBuilder.build(tmp, m_colorGenerator.getColor(title)));
holder.textChecked.setVisibility(View.GONE);
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
Cursor article = (Cursor)getItem(position);
final Cursor article = (Cursor)getItem(position);
HeadlineViewHolder holder;
final HeadlineViewHolder holder;
final int articleId = article.getInt(0);
@ -544,6 +570,8 @@ public class OfflineHeadlinesFragment extends Fragment implements OnItemClickLis
holder.flavorImageLoadingBar = (ProgressBar) v.findViewById(R.id.flavorImageLoadingBar);
holder.flavorImageArrow = v.findViewById(R.id.flavorImageArrow);
holder.headlineFooter = v.findViewById(R.id.headline_footer);
holder.textImage = (ImageView) v.findViewById(R.id.text_image);
holder.textChecked = (ImageView) v.findViewById(R.id.text_checked);
v.setTag(holder);
@ -563,6 +591,31 @@ public class OfflineHeadlinesFragment extends Fragment implements OnItemClickLis
});
}
if (holder.textImage != null) {
updateTextCheckedState(holder, article);
holder.textImage.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
Log.d(TAG, "textImage : onclicked");
SQLiteStatement stmtUpdate = m_activity.getWritableDb().compileStatement("UPDATE articles SET selected = NOT selected " +
"WHERE " + BaseColumns._ID + " = ?");
stmtUpdate.bindLong(1, articleId);
stmtUpdate.execute();
stmtUpdate.close();
updateTextCheckedState(holder, article);
refresh();
m_activity.invalidateOptionsMenu();
}
});
}
if (holder.titleView != null) {
holder.titleView.setText(Html.fromHtml(article.getString(article.getColumnIndex("title"))));

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -10,13 +10,25 @@
android:background="?headlineNormalBackground"
tools:ignore="HardcodedText" >
<CheckBox
android:id="@+id/selected"
<FrameLayout
android:layout_marginRight="8dp"
android:layout_gravity="center_vertical|left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:layout_gravity="center"
android:focusable="false" />
android:layout_height="wrap_content">
<ImageView
android:clickable="true"
android:layout_width="48dp"
android:layout_height="48dp"
android:id="@+id/text_image"/>
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/check_sm"
android:id="@+id/text_checked"/>
</FrameLayout>
<LinearLayout
android:layout_weight="1"

View File

@ -10,13 +10,25 @@
android:background="?headlineSelectedBackground"
tools:ignore="HardcodedText" >
<CheckBox
android:id="@+id/selected"
<FrameLayout
android:layout_marginRight="8dp"
android:layout_gravity="center_vertical|left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:layout_gravity="center"
android:focusable="false" />
android:layout_height="wrap_content">
<ImageView
android:clickable="true"
android:layout_width="48dp"
android:layout_height="48dp"
android:id="@+id/text_image"/>
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/check_sm"
android:id="@+id/text_checked"/>
</FrameLayout>
<LinearLayout
android:layout_weight="1"

View File

@ -10,13 +10,25 @@
android:background="?headlineSelectedBackground"
tools:ignore="HardcodedText" >
<CheckBox
android:id="@+id/selected"
<FrameLayout
android:layout_marginRight="8dp"
android:layout_gravity="center_vertical|left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:layout_gravity="center"
android:focusable="false" />
android:layout_height="wrap_content">
<ImageView
android:clickable="true"
android:layout_width="48dp"
android:layout_height="48dp"
android:id="@+id/text_image"/>
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/check_sm"
android:id="@+id/text_checked"/>
</FrameLayout>
<LinearLayout
android:layout_weight="1"

View File

@ -10,13 +10,25 @@
android:background="?headlineUnreadBackground"
tools:ignore="HardcodedText" >
<CheckBox
android:id="@+id/selected"
<FrameLayout
android:layout_marginRight="8dp"
android:layout_gravity="center_vertical|left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:layout_gravity="center"
android:focusable="false" />
android:layout_height="wrap_content">
<ImageView
android:clickable="true"
android:layout_width="48dp"
android:layout_height="48dp"
android:id="@+id/text_image"/>
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/check_sm"
android:id="@+id/text_checked"/>
</FrameLayout>
<LinearLayout
android:layout_weight="1"