I created a custom RelativeLayout and I want to populate it with a design from an xml file, is it possible?
My code:
This is the onCreate from my main activity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new CustomRelativeLayout(this));
getLayoutInflater().inflate(R.layout.three_items_list_row, null, false);
}
Just call addView for your custom RelativeLayout instance,
I must say it not a very good coding because you add a Layout level
You need to modify your class to this:
class CustomRelativeLayout{
public CustomRelativeLayout(Context context, AttributeSet attrs, int defStyle){
super(context,attrs,defStyle);
init();
}
public CustomRelativeLayout(Context context, AttributeSet attrs){
super(context,attrs);
init();
}
public CustomRelativeLayout(Context context){
super(context);
init();
}
private void init(){
LayoutInflater inflater = (LayoutInflater) context.getSystemService( Context.LAYOUT_INFLATER_SERVICE );
View v = inflatet.inflate(R.layout.three_items_list_row, this, false);
addView(v);
}
}
Related
I have 2 classes: MainActivity and CustomView. I have an XML layout with this CustomView.
I want to access all my MainActivity variables from my CustomView class and also to modify them, I tried to get the context but it didn't work.
MainActivity class:
MyCustomView customV;
int myVar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.start_page);
customV = (MyCustomView) findViewById(R.id.view2);
myVar = 5;
}
MyCustomView class:
public class MyCustomView extends TextView {
public MyCustomView(Context context) {
super(context);
init();
}
public MyCustomView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MyCustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
context.myVar = 7 //this is what I'm trying to do...
I also tried getContext which didn't work.
By trying to access variables from your Activity directly in your TextView subclass, you introduce tight coupling between you subclass of Actity and your custom TextView, which essentially hinders the reusability of your custom TextView because it can then only be used in that type of Activity, using it in another layout would break everything. Basically, I would say it's bad design and would not recommend that approach.
You could simply add a method to your custom TextView, and use the variable there :
public class MyCustomView extends TextView {
// your code
public void setVariable(int myInt){
//use the int here, either set a member variable
//or use directly in the method
}
}
and in your Activity
customV = (MyCustomView) findViewById(R.id.view2);
customV.setVariable(5);
Best way to do so is,
public class MyCustomView extends TextView {
private MainActivity mActivity;
public void setActivity(MainActivity activity) {
mActivity = activity;
}
public MyCustomView(Context context) {
super(context);
init();
}
public MyCustomView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MyCustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
}
Instantiate,
customView = (MyCustomView) findViewById(R.id.view2);
customView.setActivity(this);
Access variable inside functions in MyCustomView,
mActivity.myVar = 10;
If you don't care about the fact that you are tightly coupling your two classes, then the only thing you need to do is typecast your "context" variable to MainActivity. Something like:
((MainActivity) context).myVar = 5;
will work. But you really should consider an alternate design that only loosely couples your classes.
or make the variable static and use it like this.
MainActivity.myVar=7;
or if you want to initialize in the custom view depend on a variable from the MainActivity then do it like that in the custom view.
private int myVar;
public void init(int myVar) {
this.myVar = myVar;
invalidate();
in MainActivity.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.start_page);
customV = (MyCustomView) findViewById(R.id.view2);
myVar = 5;
customV.init(myVar);
}
i hope this help you.
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.
I have extended BaseActivity from ActionBarActivity, in which I set the activity's content. There's a FrameLayout in the layout file I use.
When I extend BaseActivity to use in e.g. MainActivity, I'd like MainActivity to inflate the FrameLayout with a custom layout file.
I couldn't come up with a solution. I always got errors. This is how far I came.
BaseActivity.java
public class BaseActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.base);
}
}
MainActivity.java
public class MainActivity extends BaseActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
FrameLayout mFrame = (FrameLayout) findViewById(R.id.content);
mFrame.addView(LayoutInflater.from(context).inflate(R.layout.activity_nav_test, mFrame, true));
return super.onCreateView(parent, name, context, attrs);
}
Thanks a lot for your help!
Chris
Your approach seems to be proper but add view inside frame layout in onCreate method instead.
public class MainActivity extends BaseActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FrameLayout mFrame = (FrameLayout) findViewById(R.id.content);
mFrame.addView(LayoutInflater.from(this).inflate(R.layout.activity_nav_test, mFrame, true));
}
In this method :
#Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
FrameLayout mFrame = (FrameLayout) findViewById(R.id.content);
mFrame.addView(LayoutInflater.from(context).inflate(R.layout.activity_nav_test, mFrame, true));
return super.onCreateView(parent, name, context, attrs);
}
You must return ur custom view. Try this. I hope it helps
return mFrame;
I have a custom TextView which I'm using in a custom GridView adapter. The custom TextView is using a custom font for translation purposes. This works fairly well in devices which have the locale installed by default.
However, in devices, in which the language is not installed, it is displaying a strange behavior. When the app loads the first time, the TextViews don't display with the custom font. However when I press the refresh button to reload the fragment, the TextViews display with the custom font.
I'm not sure why this is happening.
This is happening with all the custom Adapters in my application in which I'm using the custom TextView.
Pretty basic adapter:
public class CalendarWeekAdapter extends BaseAdapter{
private String[] weekdays;
Context mContext;
private LayoutInflater mInflater;
public CalendarWeekAdapter(Context context, int firstDay)
{
mContext=context;
mInflater = LayoutInflater.from(context);
weekdays = context.getResources().getStringArray(R.array.weekdays);
}
public int getCount()
{
return weekdays.length;
}
public Object getItem(int position)
{
return position;
}
public long getItemId(int position)
{
return position;
}
public View getView(int position, View convertView, ViewGroup parent)
{
ViewHolder holder=null;
if(convertView==null)
{
convertView = mInflater.inflate(R.layout.calendar_week, parent,false);
holder = new ViewHolder();
holder.txtWeekdays=(CustomTextView)convertView.findViewById(R.id.weekdays);
if(position==0)
{
convertView.setTag(holder);
}
}
else
{
holder = (ViewHolder) convertView.getTag();
}
holder.txtWeekdays.setText(weekdays[position]);
return convertView;
}
}
class ViewHolder
{
CustomTextView txtWeekdays;
}
Basic CustomTextView:
public class CustomTextView extends TextView {
public CustomTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public CustomTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomTextView(Context context) {
super(context);
init();
}
private void init() {
if (!isInEditMode()) {
setTypeface(Utils.getFont(getContext()));
}
}
}
This is may not be the answer, but i don't understand this if-statement:
holder.txtWeekdays=(CustomTextView)convertView.findViewById(R.id.weekdays);
if(position==0)
{
convertView.setTag(holder);
}
Why is it there? All your newly inflated convertViews should have holder as their tag.
Just remove the 'if' around the convertView.setTag(holder):
if(convertView==null)
{
convertView = mInflater.inflate(R.layout.calendar_week, null,false);
holder = new ViewHolder();
holder.txtWeekdays=(CustomTextView)convertView.findViewById(R.id.weekdays);
convertView.setTag(holder);
}
...
and see if this will improve or even fix your situation.
I'm writing an app the gets information off an RSS feed, parses it using the DOM parser (issues with the clients RSS feed) and then shows the parsed objects in a ListView.
For some reason, the getView() is not being called...
here is the code for the Activity:
public class NoPicList extends Activity {
ListView list;
NoPicAdapterV2 adapter2;
ProgressDialog mDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.no_pic_list);
list = (ListView) findViewById(R.id.noPicListView);
// get the request from the main activity
Bundle b = getIntent().getExtras();
String request = b.getString("REQUEST");
mDialog = new ProgressDialog(this);
mDialog.setCancelable(false);
mDialog.setMessage("Lodaing Data");
mDialog.show();
new GetNewsAndCalendar().execute(request);
}
#Override
protected void onPause() {
mDialog.dismiss();
super.onPause();
}
class GetNewsAndCalendar extends
AsyncTask<String, Void, ArrayList<Message>> {
#Override
protected ArrayList<Message> doInBackground(String... params) {
String url = params[0];
DOMFeedParser parser = new DOMFeedParser(url);
return parser.parse();
}
#Override
protected void onPostExecute(ArrayList<Message> result) {
adapter2 = new NoPicAdapterV2(NoPicList.this, R.layout.no_pic_list_item, result);
list.setAdapter(adapter2);
mDialog.dismiss();
}
}
}
here is the code for the adapter:
public class NoPicAdapterV2 extends ArrayAdapter<NewAndCalendar> {
private ArrayList<Message> data;
private LayoutInflater inflator;
private Activity mActivity;
public NoPicAdapterV2(Context context, int textViewResourceId,
ArrayList<Message> result) {
super(context, textViewResourceId);
data = (ArrayList<Message>) result;
mActivity = (Activity) context;
inflator = (LayoutInflater) mActivity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View vi = convertView;
if (vi == null)
vi = inflator.inflate(R.layout.no_pic_list_item, parent, false);
TextView title = (TextView) vi.findViewById(R.id.noPicTitle);
TextView subtitle = (TextView) vi.findViewById(R.id.noPicSubtitle);
TextView id = (TextView) vi.findViewById(R.id.noPicID);
title.setText(data.get(position).getTitle().toString());
subtitle.setText(data.get(position).getDate().toString());
id.setText(data.get(position).getDescription());
return vi;
}
}
the DOMFeedParser code is built according to the IBM open source Java XML parser article.
thanks a bunch...
you should override (in your custom adapter)
getCount()
and return the size of data in order to let the adpater works. Or you call the ArrayAdapter constructor that takes data
public ArrayAdapter (Context context, int textViewResourceId, T[] objects)
Your ArrayAdapter does not know the content of your array, and therefore the count is 0.
Use this super constructor instead : http://developer.android.com/reference/android/widget/ArrayAdapter.html#ArrayAdapter%28android.content.Context,%20int,%20java.util.List%3CT%3E%29
Like this :
public NoPicAdapterV2(Context context, int textViewResourceId,
ArrayList<Message> result) {
// Here
super(context, textViewResourceId, result);
data = (ArrayList<Message>) result;
mActivity = (Activity) context;
inflator = (LayoutInflater) mActivity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
I hope below code will work just add one more argument to super(...)
public NoPicAdapterV2(Context context, int textViewResourceId,
ArrayList<Message> result) {
super(context, textViewResourceId,result);
data = (ArrayList<Message>) result;
mActivity = (Activity) context;
inflator = (LayoutInflater) mActivity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}