Custom ImageView class onClick change imageResource - java

I've created a custom ImageView class, and I'm trying to change the imageResource when user click on it, but I'm able to call setImageResource() from that class.
Also I'd like to store like a second imageView I mean, my custom ImageView has the same starter imageView resource, but when click on it it have to be dynamic ImageView for instance:
ImageView1 ic_launcher (user has not clicked on it)
ImageView1 ic_user (user has clicked on it)
Can you guide how to achieve this?
This is my custom ImageView class :
public class CustomImageView extends android.support.v7.widget.AppCompatImageView implements View.OnClickListener {
private View.OnClickListener clickListener;
public CustomImageView(Context context) {
super(context);
setOnClickListener(this);
}
public CustomImageView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
setOnClickListener(this);
}
public CustomImageView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setOnClickListener(this);
}
#Override
public void setOnClickListener(OnClickListener l) {
if (l == this) {
super.setOnClickListener(l);
} else {
clickListener = l;
}
}
#Override
public void onClick(View v) {
//Should change the imageResource here but also I should have to change it again if user wants (to the initial one)
if (clickListener != null) {
clickListener.onClick(this);
}
}
}

A solution for a custom toggleable ImageView:
Custom attribute in values/attr.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ToggleImageView">
<attr name="low_img" format="reference" />
<attr name="high_img" format="reference" />
</declare-styleable>
</resources>
Custom ImageView class:
public class ToggleImageView extends AppCompatImageView implements View.OnClickListener {
private Drawable mLowDrawable, mHighDrawable;
private boolean isLow = true;
public ToggleImageView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
// Extract drawables from custom attributes
TypedArray values = context.obtainStyledAttributes(attrs, R.styleable.ToggleImageView);
setLowDrawable(values.getDrawable(R.styleable.ToggleImageView_low_img));
setHighDrawable(values.getDrawable(R.styleable.ToggleImageView_high_img));
values.recycle();
setImageDrawable(mLowDrawable);
super.setOnClickListener(this);
}
public void setLowDrawable(Drawable drawable) {
mLowDrawable = drawable;
if (isLow)
setImageDrawable(mLowDrawable);
}
public void setHighDrawable(Drawable drawable) {
mHighDrawable = drawable;
if (!isLow)
setImageDrawable(mHighDrawable);
}
#Override
public void setOnClickListener(#Nullable OnClickListener l) {
// Do nothing to block setting listener from outer caller
}
#Override
public void onClick(View view) {
toggle();
}
public void toggle() {
isLow = !isLow;
setImageDrawable(isLow ? mLowDrawable : mHighDrawable);
}
}
Usage in xml layout:
<?xml version="1.0" encoding="utf-8" ?>
<FrameLayout 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:id="#+id/root_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.tamhuynh.testfragment.ToggleImageView
android:id="#+id/toggle_img"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center"
app:high_img="#mipmap/high_drawable"
app:low_img="#drawable/low_drawable"
tools:low_img="#drawable/low_drawable" />
</FrameLayout>

Related

Why is the onDraw Not Being Called in Custom View for Android?

I am developing in Android Canvas.
But the onDraw() has not called after invalidate();
In the layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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:orientation="vertical"
tools:context="com.wen.MainActivity">
<com.wen.Drawpath_view
android:id="#+id/view_path"
android:layout_weight="0.7"
android:layout_width="match_parent"
android:layout_height="0dp"/>
<RelativeLayout
android:layout_weight="0.3"
android:layout_width="match_parent"
android:layout_height="0dp">
<Button
android:id="#+id/test"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
</LinearLayout>
In the MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private static final String TAG = "MainActivity";
private Drawpath_view View_path;
private Button test_btn;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
View_path = findViewById(R.id.view_path);
View_path = new Drawpath_view(MainActivity.this);
test_btn = findViewById(R.id.test);
test_btn.setOnClickListener(this);
View_path.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
View_path.getViewTreeObserver().removeOnPreDrawListener(this);
View_path.setMinimumHeight(View_path.getHeight());
View_path.setMinimumWidth(View_path.getWidth());
return true;
}
});
}
#Override
public void onClick(View v) {
if (v.getId() == R.id.test){
View_path.Testdraw();
}
}
}
In the customer View.
public class Drawpath_view extends View {
private static final String TAG = "view";
private Paint paint;
private Path path = new Path();
public Drawpath_view(Context context) {
super(context);
setWillNotDraw(false);
initView();
}
public Drawpath_view(Context context, AttributeSet attrs) {
super(context, attrs);
}
private void initView() {
paint = new Paint();
paint.setAntiAlias(true);
paint.setDither(true);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.GREEN);
paint.setStrokeWidth(3);
}
public void Testdraw(){
Log.d(TAG,"draw");
path.moveTo(60,60);
path.lineTo(460,460);
invalidate();
}
#Override
protected void onDraw(Canvas canvas) {
Log.d(TAG,"onDraw");
super.onDraw(canvas);
canvas.drawPath(path,paint);
}
}
When I click the button in Activity. It will call
View_path.Testdraw();
But the onDraw in Drawpath_view has not been called. Did I missing something ?
Thanks in advance.
You can remove View_path = new Drawpath_view(MainActivity.this); from your code,
as you already attached view in xml.
By default all ViewGroup sub-classes do not call their onDraw method, you will enable it by calling setWillNotDraw(false) on View_path.
Add setWillNotDraw(false); initView();
into the second constructor as well.

Custom Font for an Android App

I tried to add a custom font in my app. The method creating a typeface object and putting a font in it worked. Now I want to make a class for the custom font for having a cleaner code.
CustomFont.java
public class CustomFont extends AppCompatActivity {
private final Typeface typeface = Typeface.createFromAsset(getAssets(), "fonts/Slabo.ttf");
public CustomFont(TextView textView) {
textView.setTypeface(typeface);
}
}
and now I am trying to add this font to a textview:
public class MainActivity extends AppCompatActivity {
private Typeface typeface;
private CustomFont customFont;
private TextView textview;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//typeface = Typeface.createFromAsset(getAssets(), "fonts/Slabo.ttf"); // works
textview = (TextView) findViewById(R.id.textviewTest);
//textview.setTypeface(typeface); // works
customFont = new CustomFont(textview); // does not work
}
}
but if I run this project I get this exception:
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.AssetManager android.content.Context.getAssets()' on a null object reference
Try This :
TextView text = (TextView) findViewById(R.id.custom_font);
Typeface font = Typeface.createFromAsset(getAssets(), "yourfont.ttf");
text.setTypeface(font);
you can make method like below in your commonUtill class for better clarification
public static void setTypeface(Context mContext, View view, VIEW_TYPE type,
TYPE_FACE face, int bold) {
View mView = view;
Typeface tface = getTypeface(mContext, face);
switch (type) {
case TEXTVIEW:
((TextView) mView).setTypeface(tface, bold);
break;
case BUTTON:
((Button) mView).setTypeface(tface, bold);
break;
case EDITTEXT:
((EditText) mView).setTypeface(tface, bold);
break;
case RADIOBUTTON:
((RadioButton) mView).setTypeface(tface, bold);
break;
case CHECKBOX:
((CheckBox) mView).setTypeface(tface, bold);
break;
case CHECKEDTEXTVIEW:
((CheckedTextView) mView).setTypeface(tface, bold);
break;
default:
break;
}
}
and create function like this
public static enum TYPE_FACE {
CALIBRI, ICON_FONT, BEBAS, AWESOME, BT, TAGLINE,
CALLIBRI, WEBLYSLEEK, WEBLYSLEEK_BOLD, ICON_FONT1
}
and your view method.
public static enum VIEW_TYPE {
TEXTVIEW, BUTTON, EDITTEXT, RADIOBUTTON, CHECKBOX, CHECKEDTEXTVIEW
}
by this way you can easily manage your code.
first of all CustomFont should not be activity.It should be normal class.
public class CustomFont {
private Typeface typeface;
public CustomFont(Context context) {
typeface = Typeface.createFromAsset(context.getAssets(), "fonts/Slabo.ttf");
}
public void setFont(TextView textView) {
textView.setTypeface(typeface);
}
}
And call it from your MainActivity
customFont = new CustomFont(this);
customFont.setFont(textview);
Create Java class like this..
public class TextViewKarlaBold extends TextView {
public TextViewKarlaBold(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public TextViewKarlaBold(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public TextViewKarlaBold(Context context) {
super(context);
init();
}
public void init() {
Typeface tf = Typeface.createFromAsset(getContext().getAssets(),
"fonts/Karla-Bold.ttf");
setTypeface(tf, 1);
}
}
Use this class as View in XML
<hammerapps.views.TextViewKarlaBold // hammerapps.views is My Package name
android:id="#+id/txtVIEW"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:text="VIEW"
android:textColor="#color/black"
android:textSize="12dp" />
Use this library if you want your entire application in a specific font
Check here
Use Custom Textview
public class TextView extends android.widget.TextView {
Context mContext;
String str;
boolean isCorner;
//fonts
public static Typeface Font_name;
public TextView(Context context) {
super(context);
mContext=context;
initialiseFont(null);
}
public TextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext=context;
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TextView, 0, 0);
try {
str = ta.getString(R.styleable.TextView_font_family);
isCorner=ta.getBoolean(R.styleable.TextView_isCorner,false);
} finally {
ta.recycle();
}
initialiseFont(str);
}
private void initialiseFont(String font) {
if(font==null || font.equals("")){
}
else {
Font_name = Typeface.createFromAsset(mContext.getAssets(), "DroidSansFallbackanmol256.ttf");
setTypeface(Font_name);
}
}
Use attrs.xml
<declare-styleable name="TextView">
<attr name="font_family" format="string"/>
<attr name="isCorner" format="boolean"/>
</declare-styleable>

GridView row not inflating

I have a simple GridView with custom Adapter in my layouts. My code is as follows:
CircleActivity.java:
public class CircleActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_circle);
List<Integer> listColors = new ArrayList<>();
listColors.add(getResources().getColor(R.color.colorAccent));
listColors.add(getResources().getColor(R.color.colorPrimary));
listColors.add(getResources().getColor(R.color.colorPrimaryDark));
GridView gridView = (GridView) findViewById(R.id.grid_colors);
CircleAdapter adapter = new CircleAdapter(this,listColors);
gridView.setAdapter(adapter);
}
}
activity_circle.xml:
<?xml version="1.0" encoding="utf-8"?>
<GridView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/grid_colors"
android:layout_width="match_parent"
android:layout_height="match_parent" />
CircleAdapter.java:
public class CircleAdapter extends BaseAdapter{
private Context context;
private List<Integer> listColor;
public CircleAdapter(Context context, List<Integer> listColor) {
this.listColor = listColor;
this.context = context;
}
#Override
public int getCount() {
return listColor.size();
}
#Override
public Integer getItem(int position) {
return listColor.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if(convertView==null){
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.row_grid,parent,false);
holder = new ViewHolder(convertView);
convertView.setTag(holder);
}
else{
holder = (ViewHolder) convertView.getTag();
}
holder.customCircleView.setFillColor(listColor.get(position));
holder.customCircleView.setCircleRadius(100);
return convertView;
}
static class ViewHolder{
private CustomCircleView customCircleView;
public ViewHolder(View row){
customCircleView = (CustomCircleView) row.findViewById(R.id.custom_circle_view);
}
}
}
row_grid.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:background="#color/colorAccent"
android:layout_width="match_parent"
android:gravity="center"
android:layout_height="match_parent">
<com.droidexperiments.gridexpand.CustomCircleView
android:id="#+id/custom_circle_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
custom:fill_color="#color/colorPrimary"
custom:circle_radius="50"
android:padding="25dp" />
</LinearLayout>
CircleView.java:
public class CustomCircleView extends View {
private int circleRadius = 20;
private int fillColor = Color.BLACK;
public CustomCircleView(Context context) {
super(context);
}
public CustomCircleView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public CustomCircleView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CustomCircleView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(R.styleable.CustomCircle);
circleRadius = typedArray.getInteger(R.styleable.CustomCircle_circle_radius,20);
fillColor = typedArray.getColor(R.styleable.CustomCircle_fill_color, Color.BLACK);
typedArray.recycle();
}
public int getCircleRadius() {
return circleRadius;
}
public void setCircleRadius(int circleRadius) {
this.circleRadius = circleRadius;
}
public int getFillColor() {
return fillColor;
}
public void setFillColor(int fillColor) {
this.fillColor = fillColor;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(fillColor);
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(canvas.getWidth()/2,canvas.getHeight()/2,circleRadius,paint);
}
}
attrts.xml:
<declare-styleable name="CustomCircle">
<attr name="fill_color" format="reference|color"/>
<attr name="circle_radius" format="integer"/>
</declare-styleable>
The issue is that screen remains blank and no row is inflated/showing in GridView.
I have checked everything. There is not any issue in GridView or the layout of grid row or in CustomCircleView. If I change adapter to simple ArrayAdapter, it works fine. So, there must be issue with my adapter:
I double checked getView() in adapter;
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if(convertView==null){
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.row_grid,parent,false);
holder = new ViewHolder(convertView);
convertView.setTag(holder);
}
else{
holder = (ViewHolder) convertView.getTag();
}
holder.customCircleView.setFillColor(listColor.get(position));
holder.customCircleView.setCircleRadius(100);
return convertView;
}
but couldn't identify why it shows blank. can anyone help me please?
When you implement a custom view, it is essential that you implement onMeasure. This method will tell the Android framework what size your view should be. Since you didn't specify this for CustomCircleView and used wrap_content in your layout, it had size zero. Therefore, all the elements of the GridView were invisible, making it look like the adapter was not working. I made a simple example implementation of onMeasure that solves your problem (just add this method in CustomCircleView):
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int size = View.MeasureSpec.makeMeasureSpec(2 * this.circleRadius, MeasureSpec.EXACTLY);
setMeasuredDimension(size, size);
}
The documentation advises that size stays within the given parameters (widthMeasureSpec and heightMeasureSpec). I have not included that restriction here, you can determine yourself what you want to do in that case.
You can find more information about this in the guide on creating custom components. Specific information about onMeasure can be found here.

Changing font in Listview without using extra adapter class

I am not able to change the fonts of rows in a listview. I am not using extra adapter class and rows are populated statically. I am wondering how can I change fonts of rows. Below you can find my code for that.
public class Program extends Activity {
private String[] gunler =
{"Studio 1", "Studio 2", "Spinning", "Cross Fit"};
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
ActionBar actionBar = getActionBar();
setContentView(R.layout.activity_program);
//(A) adımı
ListView listemiz=(ListView) findViewById(R.id.listView1);
//(B) adımı
ArrayAdapter<String> veriAdaptoru=new ArrayAdapter<String>
(this, android.R.layout.simple_list_item_1, android.R.id.text1, gunler);
final Typeface font = Typeface.createFromAsset(getAssets(),
"futura-condensed-medium.ttf");
//(C) adımı
listemiz.setAdapter(veriAdaptoru);
listemiz.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
Intent intent = new Intent(Program.this, ProgramDetail.class);
int keyIdentifer = -1;
intent.putExtra("kacinciGun", new Integer(position+1).toString());
startActivity(intent);
}
});
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
return super.onOptionsItemSelected(item);
}
}
and layout file:
<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"
tools:context=".MainActivity" >
<ListView
android:id="#+id/listView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" >
</ListView>
</RelativeLayout>
You can do this using a custom TextView where you set the font there.
public class CustomTextView extends TextView {
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);
}
public void init(Context context) {
Typeface typeface = Typeface.createFromAsset(getContext().getAssets(), "your font name here");
setTypeface(typeface);
}
}
Then create a new layout using this new textview
<com.your.package.CustomTextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
Then in your activity create the adapter like so
ArrayAdapter<String> veriAdaptoru = new ArrayAdapter<String>
(this, R.layout.some_layout, R.id.text, gunler);

Custom View children are null after inflating the view in Android

I am working on an Android app. I have a custom view and layout as follows:
<com.hello.view.card.inner.SimpleCardView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/card_simple_linear_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="#+id/simple_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</com.hello.view.card.inner.SimpleCardView>
And this is the Java class:
public class SimpleCardView extends LinearLayout {
protected SimpleCard card = null;
protected TextView textView;
public SimpleCardView(Context context) {
super(context);
init(context);
}
public SimpleCardView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public SimpleCardView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
protected void init(Context context) {
textView = (TextView)findViewById(R.id.simple_label);
}
public void setCard(SimpleCard card) {
this.card = card;
textView.setText(card.getMessage());
}
}
And this is how I am inflating the view (I tried both following calls):
SimpleCardView view = (SimpleCardView)inflater.inflate(R.layout.card_simple, null);
//SimpleCardView view = (SimpleCardView)inflater.inflate(R.layout.card_simple, parent);
view.setCard(card);
The problem I am having is when view.setCard(card) is called, I see that textView is null, even though I am expecting it to be set in the init(..) method. Can anyone tell me what it not being set correctly? Thanks in advance.
Thank you for your answers. It turns out init(context) should not be called in the constructor. The right place to call it is in onFinishInflate(). The following change helped fix it:
public class SimpleCardView extends LinearLayout {
protected SimpleCard card = null;
protected TextView textView;
public SimpleCardView(Context context) {
super(context);
}
public SimpleCardView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SimpleCardView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
init(getContext());
}
protected void init(Context context) {
textView = (TextView)findViewById(R.id.simple_label);
}
public void setCard(SimpleCard card) {
this.card = card;
textView.setText(card.getMessage());
}
}
Instead of using root element
com.hello.view.card.inner.SimpleCardView
try using
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:id="#+id/simple_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</merge>
then, in your init method
LayoutInflater.from(context).inflate(R.layout.card_simple, this);
setOrientation(LinearLayout.VERTICAL);
textView = (TextView)findViewById(R.id.simple_label);
When you use the view in other layouts, that's where you will want to put
<com.hello.view.card.inner.SimpleCardView />
and any properties that it needs, its id, its width/height, etc.

Categories