Androidplot - creating chart without XML - pie-chart

I need to create some PieCharts on my Activity, and quantity of charts varies.
I try to create PieChart dynamically from code, and it's doesn't work. My Layout correct - Button added successfully. Can you help me? Some code:
Layout:
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context="example.bros.nik.tabs1.MealsActivity"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/l_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin"
android:orientation="vertical" >
</LinearLayout>
</ScrollView>
Java:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_meals);
LinearLayout l_layout = (LinearLayout) findViewById(R.id.l_layout);
meals_params = ResultsActivity.get_meals_params();
LinearLayout.LayoutParams lp_view = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT, 1.0f);
PieChart pie = new PieChart(this, "Chart#1");
Segment s1 = new Segment("Углеводы", meals_params[0].get_carbohydrates_percent());
Segment s2 = new Segment("Жиры", meals_params[0].get_fats_percent());
Segment s3 = new Segment("Белки", meals_params[0].get_proteins_percent());
EmbossMaskFilter emf = new EmbossMaskFilter(new float[]{1, 1, 1}, 0.4f, 10, 8.2f);
SegmentFormatter sf1 = new SegmentFormatter();
sf1.configure(getApplicationContext(), R.xml.pie_segment_formatter1);
sf1.getFillPaint().setMaskFilter(emf);
SegmentFormatter sf2 = new SegmentFormatter();
sf2.configure(getApplicationContext(), R.xml.pie_segment_formatter2);
sf2.getFillPaint().setMaskFilter(emf);
SegmentFormatter sf3 = new SegmentFormatter();
sf3.configure(getApplicationContext(), R.xml.pie_segment_formatter3);
sf3.getFillPaint().setMaskFilter(emf);
pie.addSeries(s1, sf1);
pie.addSeries(s2, sf2);
pie.addSeries(s3, sf3);
pie.getRenderer(PieRenderer.class).setDonutSize(0.1f,PieRenderer.DonutMode.PERCENT);
pie.redraw();
pie.getBorderPaint().setColor(Color.TRANSPARENT);
pie.getBackgroundPaint().setColor(Color.TRANSPARENT);
l_layout.addView(pie, lp_view);
I try this:
pie.setLayoutParams(lp_view);
l_layout.addView(pie);
and it's doesn't help me.
And if I add Button to Layout:
pie.setLayoutParams(lp_view);
l_layout.addView(pie);
Button but = new Button(this);
but.setText("bla");
l_layout.addView(but, lp_view);
then I see this Button (look at screenshot).
Added button
EDIT:
In Android docs written, that in constructor
ViewGroup.LayoutParams (int width, int height)
attributes may be the absolute sizes of view, or may be constants - WRAP_CONTENT (int value -2), MATCH_PARENT (int value -1). Probably, PieChart doesn't recognize these constants, and get it as absolute size with negative values.
I found solution:
private int[] getDisplayParams() {
int[] display_params = new int[2];
Display display = getWindowManager().getDefaultDisplay();
if (Build.VERSION.SDK_INT >= 13) {
Point size = new Point();
display.getSize(size);
display_params[0] = size.x;
display_params[1] = size.y;
}
else {
display_params[0] = display.getWidth();
display_params[1] = display.getHeight();
}
return display_params;
}
and use size of screen for determining size of chart:
int[] display_params = getDisplayParams();
Double pie_size = display_params[0]*0.8;
int pie_size_int = pie_size.intValue();
ViewGroup.LayoutParams lp_view = new LinearLayout.LayoutParams(pie_size_int, pie_size_int);

Looks like you are not providing LayoutParams for pie. Try doing this before adding pie to l_layout:
pie.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.FILL_PARENT,
LinearLayout.LayoutParams.FILL_PARENT));
EDIT:
I went back and noticed that the layout is enclosed in a ScrollView but the Scrollview is set to wrap content and the content is set to match parent so it seems like it might be a chicken and egg problem as described here. I believe buttons, etc. have default non-zero values for these whereas plots do not, which might explain why the button works.
As a sanity check, what happens if you specify an absolute size for your plot like this:
LinearLayout.LayoutParams lp_view = new LinearLayout.LayoutParams(100, 100);
You can also try:
plot.setMinimumHeight(100);
plot.setMinimumWidth(100);

Problem resolved by Nick.
You can also try:
plot.setMinimumHeight(100);
plot.setMinimumWidth(100);

Related

Android how to get location of a html element for using in positioning a android view over it

In Android layout it has a RelativeLayout which contains a LinearLayout (ignored some other views in the layout).
< ...> //other layout
<NestedScrollView
android:id="#+id/scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
android:fillViewport="false">
<RelativeLayout
android:id="#+id/view_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="0dp">
<LinearLayout
android:id="#+id/list_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
/>
</RelativeLayout>
</NestedScrollView>
<...>//other layout
the list_container could have a few views, one is webView.
What want to do is in the html loaded in the webview, there is a div element, so the android side could locate this div and float a view on top of that div element (the <div> is treat as a location marker).
making it simpler, assuming there is only one webview in the list_container, so the origin of the webview view is same as the relativeLayout's left/top.
and there is some floating View that is child of the RelativeLayout (sibling of the list_container), so the idea is set the floatingView's left/top margin to place the floatingView over of the <div> element in the html.
here use the webview.evaluateJavascript to run a javascript, and get the element's location back through the callback(got from js's document.getElementById(elm.id).getBoundingClientRect(),
after that, just apply the left/top to the floating view's margin.
val strJs = "(function() {\n" +
"var elementId = 'element_div_id';\n" +
"try {\n" +
" var elm = document.getElementById(elementId);\n" +
" var rect = document.getElementById(elm.id).getBoundingClientRect();\n" +
" return {\n" +
" 'elementId': elm.id,\n" +
" 'left': Math.floor(rect.left),\n" +
" 'top': Math.floor(rect.top)\n" +
" };\n" +
"} catch (e) {}\n" +
"return '';})();"
webview.evaluateJavascript(strJs) { data ->
if (!data.isNullOrBlank() && data.isNotEmpty()) {
try {
val obj: JSONObject = JSONObject(data)
val floatViewId = obj.optString("elementId")
val left = obj.optInt("left")
val top = obj.optInt("top")
val theFloatingView = lookupFloatingViewById(floatViewId)
var lp = (theFloatingView.layoutParams)
if (lp != null) {
if (lp.leftMargin != left || lp.topMargin != top) {
lp.setMargins(left, top, 0, 0)
theFloatingView.layoutParams = lp
}
}
} catch (ex: Throwable) {
}
}
}
But the result is that the floating view is not placed at the returned html <div> element's left/top, it is off (on different device the off could be bigger or small) over different part of the html content.
Isn't it the webview and the html hosted in the webview both should have same origin (which actually same as the relativeLayout's lefty/top)? Why can't we use the left/top got from html dom's getBoundingClientRect() directly for the view's margin (for positioning the view)?
found a answer which solved similar problem.
seems the Android view are on different coordinate than the html window. (could someone confirm or anyone has different opinion?)
the location return from getBoundingClientRect() needs to remapped to android's coordinate.
final int px = (int) (x * ctx.getResources().getDisplayMetrics().density);
final int py = (int) (y * ctx.getResources().getDisplayMetrics().density);

How to create app bar with icons using TabLayout Android Design?

I'm trying to use the new TabLayout in the android design library to create app bar with icons.
public void setupTabLayout(TabLayout tabLayout) {
tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
tabLayout.setTabGravity(TabLayout.GRAVITY_CENTER);
tabLayout.setupWithViewPager(mViewpager);
tabLayout.getTabAt(0).setIcon(R.drawable.ic_tabbar_library);
tabLayout.getTabAt(1).setIcon(R.drawable.ic_tabbar_recents);
tabLayout.getTabAt(2).setIcon(R.drawable.ic_tabbar_favorites);
tabLayout.getTabAt(3).setIcon(R.drawable.ic_tabbar_notifications);
tabLayout.getTabAt(4).setIcon(R.drawable.ic_tabbar_settings);
}
Result:
Please help me to create app bar similar:
Sorry my english is not good.Thanks is advance !
From the documentation :
https://developer.android.com/reference/android/support/design/widget/TabLayout.Tab.html#setCustomView(android.view.View)
Set a custom view to be used for this tab. This overrides values set
by setText(CharSequence) and setIcon(Drawable).
you will have to set the title values yourself
From your example:
public void setupTabLayout(TabLayout tabLayout) {
tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
tabLayout.setTabGravity(TabLayout.GRAVITY_CENTER);
tabLayout.setupWithViewPager(mViewpager);
TextView tab = (TextView) LayoutInflater.from(this).inflate(R.layout.custom_tab, null);
tab.setText("Library");
tab.setCompoundDrawablesWithIntrinsicBounds(0, R.drawable.ic_tabbar_library, 0, 0);
tabLayout.getTabAt(0).setCustomView(tab);
//..
}
custom_tab.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/tab" />
Update
The api has changed to allow you to set a custom id so you don't have to set the text/drawable manually. It'll use the adapter's values.
If the provided view contains a TextView with an ID of text1 then that
will be updated with the value given to setText(CharSequence).
Similarly, if this layout contains an ImageView with ID icon then it
will be updated with the value given to setIcon(Drawable).
You can use the attribute android:layout of TabItem to set custom view.
In custom view xml file, remember to set id of icon and text view to #android:id/icon and android:id="#android:id/text1", then the library will take care the rest.
Here's an example:
. custom_tab_item.xml
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="#android:id/icon"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_marginTop="4dp"
android:scaleType="centerInside"/>
<TextView
android:id="#android:id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:textSize="16dp"/>
</LinearLayout>
. main.xml
<android.support.design.widget.TabLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.design.widget.TabItem
android:id="#+id/ti_activities"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:icon="#drawable/ic_question"
android:layout="#layout/custom_tab_item"
android:text="#string/activities"/>
<android.support.design.widget.TabItem
android:id="#+id/ti_profile"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:icon="#drawable/ic_question"
android:layout="#layout/custom_tab_item"
android:text="#string/Profile"/>
</android.support.design.widget.TabLayout>
Hope this help.
As documentation said You can add items to TabLayout through the xml by use of TabItem. An example usage is like:
<android.support.design.widget.TabLayout
android:layout_height="wrap_content"
android:layout_width="match_parent">
<android.support.design.widget.TabItem
android:text="#string/tab_text"/>
<android.support.design.widget.TabItem
android:icon="#drawable/ic_android"/>
</android.support.design.widget.TabLayout>
When you use vector drawables as icons you might want to reuse a single drawables for different states, by simply tinting it differently. This way you can reduce the apk size and the allocation of resources. First define a custom FragmentPagerAdapter (I am using kotlin instead of java here)
class TabPagerAdapter(fm: FragmentManager) : FragmentPagerAdapter(fm) {
override fun getCount(): Int = 2
override fun getItem(position: Int): Fragment = when (position) {
0 -> FirstFragment.newInstance()
else -> SecondFragment.newInstance()
}
fun getPageIcon(context: Context, position: Int): Drawable = when (position) {
0 -> ContextCompat.getDrawable(context, R.drawable.ic_whatshot)
else -> ContextCompat.getDrawable(context, R.drawable.ic_face)
}
}
Instead of implementing getPageTitle we create a getPageIcon method, that returns a drawable for a specific tab. Next we create a custom TabLayout:
class IconTabLayout : TabLayout {
private var viewPager: ViewPager? = null
constructor(context: Context) : super(context)
constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet)
constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int) : super(context, attributeSet, defStyleAttr)
override fun onAttachedToWindow() {
if (viewPager == null) {
if (parent is ViewPager) viewPager = parent as ViewPager
}
super.onAttachedToWindow()
}
override fun setupWithViewPager(viewPager: ViewPager?, autoRefresh: Boolean) {
this.viewPager = viewPager
super.setupWithViewPager(viewPager, autoRefresh)
}
override fun addTab(#NonNull tab: Tab, position: Int, setSelected: Boolean) {
if (viewPager != null && viewPager!!.adapter is TabPagerAdapter) {
val icon: Drawable = DrawableCompat.wrap((viewPager!!.adapter as TabPagerAdapter).getPageIcon(context, position))
DrawableCompat.setTintList(icon.mutate(), ContextCompat.getColorStateList(context, R.color.tab_color))
tab.icon = icon
}
super.addTab(tab, position, setSelected)
}
}
So the magic happens in the addTab method, where the icon and the color state list are set. The color state list has following structure:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Non focused states -->
<item android:color="#color/tab_not_active" android:state_focused="false" android:state_pressed="false" android:state_selected="false" />
<item android:color="#color/tab_active" android:state_focused="false" android:state_pressed="false" android:state_selected="true" />
<!-- Focused states -->
<item android:color="#color/tab_not_active" android:state_focused="true" android:state_pressed="false" android:state_selected="false" />
<item android:color="#color/tab_active" android:state_focused="true" android:state_pressed="false" android:state_selected="true" />
<!-- Pressed -->
<item android:color="#color/tab_not_active" android:state_pressed="true" />
</selector>

is there any way to put Cardview inside Horizontalscrollview?

I have an horizontalScrollView with some card style view inside.
there are image and textview inside the card style view.
I used to do it by manually creating a new linearlayout and put the image and text inside the view programmally.
the card style code is like below:
LinearLayout layout = new LinearLayout(getActivity()
.getApplicationContext());
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(PixeltoDp(100),PixeltoDp(180));
//params.setMargins(PixeltoDp(10),PixeltoDp(5),PixeltoDp(10), PixeltoDp(5));
params.setMargins(10,5,10,5);
//layout.setLayoutParams(new LayoutParams(280,400));// single container
layout.setLayoutParams(params);
layout.setOrientation(LinearLayout.VERTICAL);
layout.setGravity(Gravity.TOP);
layout.setBackgroundResource(R.drawable.bg_card);
final ImageView imageView = new ImageView(getActivity()
.getApplicationContext());
imageView............
TextView textView = new TextView(getActivity().getApplicationContext());
...............
layout.addView(imageView);
layout.addView(textView);
return layout;
}
but now, I'd like to use android.support.v7.widget.CardView class to make this. so I can easily customize the layout of the cardview in an cardview.xml file in the layout/ folder. and my cardview layout is like this:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_gravity="center">
<ImageView
android:id="#+id/movie_img"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="0.8"
android:gravity="center_horizontal"
android:cropToPadding="false"
android:src="#drawable/ic_poster" />
<TextView
android:id="#+id/movie_title"
android:layout_width="fill_parent"
android:layout_height="25dp"
android:gravity="center"
android:ellipsize="end"
android:singleLine="true"
android:hint="synaptop"
android:textSize="10sp"
android:textColor="#e0e0e0" />
</LinearLayout>
the problem is how should i use the layout file?
I found the answer myself after all...
just inflat the view
//create ur cardview here and inflate it
final CardView mCardView = new CardView(getActivity());
View.inflate(getActivity(), R.layout.cardview_layout, mCardView);
LinearLayout mCardContainer = (LinearLayout)mCardView.findViewById(R.id.card_view_container);
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mCardContainer.setBackgroundResource(R.drawable.ripple_color_borderless);
}
//here u just define whatever u have in ur cardview xml
final ImageView mImageView = (ImageView) mCardView.findViewById(R.id.movie_img);
mImageView.setTag(index);
mImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
and the end, add this view to your horizontalScrollView.

ListView with ViewStub in Item Template - expanded controls not showing for last item

I have the following application layout:
Activity with a LinearLayout hosting a Fragment, which hosts a ListView in a LinearLayout. I've templated (not sure it's the right term - I'm coming from WPF) how the ListView is displayed. There's a 3 line part that is shown at all times, plus a ViewStub which is expanded when the item is clicked (and only one item is to be expanded at all times).
Upon the first click on each ListView item, the stub is inflated (works for all items), and then the details and myButton controls are configured. This works for all ListView items - however, for the last of them, details and myButton never show.
On top of that, if another stub is expanded, the last ListView item becomes invisible - instead of it moving down to make space for the item that is currently expanded.
So, if I click on ListView item on position myListView.Items.Count -1, I don't see anything of the expansion. If I click on any other ListView item, the last ListView item disappears.
Here's the fragment layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:minWidth="25px"
android:minHeight="25px">
<TextView
android:text="#string/active_calls"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/Calls_Header" />
<ListView
android:minWidth="25px"
android:minHeight="25px"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/ActiveCallsList" />
</LinearLayout>
And the layout for each ListView item
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TableLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/tableLayout1"
android:shrinkColumns="*"
android:stretchColumns="*">
<TableRow
android:id="#+id/tableRow1">
<TextView
android:text="Caller Name"
android:textAppearance="?android:attr/textAppearanceLarge"
android:id="#+id/CallerName"
android:layout_column="0"
android:layout_span="2" />
</TableRow>
<TableRow
android:id="#+id/tableRow2">
<TextView
android:text="+41 12 345 6789"
android:textAppearance="?android:attr/textAppearanceSmall"
android:id="#+id/CallerNumber"
android:layout_column="0" />
<TextView
android:text="00:01:25"
android:textAppearance="?android:attr/textAppearanceSmall"
android:id="#+id/CallDuration"
android:layout_column="1" />
</TableRow>
<TableRow
android:id="#+id/tableRow3">
<TextView
android:text="Ringing"
android:textAppearance="?android:attr/textAppearanceSmall"
android:id="#+id/CallStatus"
android:layout_column="0" />
</TableRow>
</TableLayout>
<ViewStub
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/viewStub1"
android:inflatedId="#+id/CallDetails"
android:layout="#layout/CallDetails" />
</LinearLayout>
and the part to be expanded
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:text="Text"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/CallDetailsText" />
<Button
android:text="#string/endCall"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/EndCallButton" />
</LinearLayout>
And the ItemClick handler associated to my own implementation of an ArrayAdapter deriving from BaseAdapter.
void calls_ItemClick(object sender, AdapterView.ItemClickEventArgs e)
{
if (inflatedView != null)
inflatedView.Visibility = ViewStates.Gone; // make sure only one item is inflated at all times
var obj = e.Parent.GetItemAtPosition(e.Position);
var listView = sender as ListView;
Model.Call t = Model.Calls.MyCalls[e.Position];
string text = t.CallerName + " " + t.CallState;
Log.Debug("SmartAppMobile", "Call " + text + " clicked");
//Toast.MakeText(this.Activity, text, ToastLength.Short).Show();
ViewStub myStub = e.View.FindViewById<ViewStub>(Resource.Id.viewStub1);
bool previouslyFound = false;
if (myStub != null)
{
inflatedView = myStub.Inflate();
}
else
{
Log.Debug("myapp", "View stub not found for " + text);
inflatedView = e.View.FindViewById<View>(Resource.Id.CallDetails);
inflatedView.Visibility = ViewStates.Visible;
previouslyFound = true;
}
TextView details = inflatedView.FindViewById<TextView>(Resource.Id.CallDetailsText);
if (details != null)
details.Text = "Call details go here... " + t.CallerNumber;
else
Log.Debug("myapp", "Call Details Text field not found for " + text);
Button myButton = inflatedView.FindViewById<Button>(Resource.Id.EndCallButton);
if (myButton != null)
{
if (!previouslyFound)
myButton.Click += (x, y) =>
{
Model.Calls.MyCalls.Remove(t);
//((ArrayAdapter)listView.Adapter).NotifyDataSetChanged();
inflatedView.Visibility = ViewStates.Gone;
};
}
}
Also, if I click on myButton, the application reacts as if I had pressed the back button on the phone - and the ListView item is only removed if it is one that I added in code.
So I guess I have to add the way I call the activity hosting the fragment, and how the ListView items are bound as well:
In my fragment hosting the ListView:
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View fragment = inflater.Inflate(Resource.Layout.CallsFragment, null);
calls = fragment.FindViewById<ListView>(Resource.Id.ActiveCallsList);
calls.Adapter = new ActiveCallsAdapter(this.Activity, Model.Calls.MyCalls);
calls.ItemClick += new EventHandler<AdapterView.ItemClickEventArgs>(calls_ItemClick);
return fragment;
}
and my Data model for the calls
public class Calls
{
private static List<Call> myCalls;
private static object myLock = new object();
public static List<Call> MyCalls
{
get
{
lock (myLock)
{
if (myCalls != null)
return myCalls;
myCalls = new List<Call>();
myCalls.Add(new Call { CallerName = "some name", CallerNumber = "some phone number", CallState = Model.CallState.Init });
myCalls.Add(new Call { CallerName = "some other name", CallerNumber = "another number", CallState = CallState.Held });
return myCalls;
}
}
}
}
And the button I use in my main activity to add a new item to the list and display the list:
Button button = FindViewById<Button> (Resource.Id.myButton);
button.Click += delegate
{
button.Text = string.Format ("{0} clicks!", count++);
Model.Calls.MyCalls.Add(new Model.Call { CallerName = "test " + count, CallerNumber = "" + count, CallState = Model.CallState.Active });
StartActivity(new Intent(this, typeof(CallsActivity)));
};
And of course, CallsActivity
[Activity(Label = "My Activity")]
public class CallsActivity : Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.CallsActivity);
// Create your application here
}
}
This is a layout bug. When you inflate the selected call, the call layout gets bigger but its parent doesn't, so the bottom one falls off the edge. That's also why it looks like the bottom one is not expanding--the expanded content is falling off the bottom. You can see this in action if you edit CallDetails.axml to set layout_height for the button to 1dp.
You can fix this layout issue by changing activeCallsList/layout_height (in CallsFragment.axml) to match_parent or fill_parent instead of wrap_content. Instead of reserving just enough space to show the calls when first drawn, activeCallsList will reserve as much space as it can. This makes your call Views behave as expected.
-Max

Table shows extra blank columns at the end

I am using jface tableViewer.When table has no data in it ,it shows all columns correctly But when Data gets added to the table it shows extra blank space or column at the end of the table.
I am using TreeViewer + TreeViewerColumn and had this problem too, this workaround might work for your TableViewer too: Programmatically set the size of the last column on parent resize:
treeViewer.getTree().addControlListener(new ControlAdapter() {
public void controlResized(ControlEvent e) {
packAndFillLastColumn();
}
});
where the action is in
// Resize last column in tree viewer so that it fills the client area completely if extra space.
protected void packAndFillLastColumn() {
Tree tree = treeViewer.getTree();
int columnsWidth = 0;
for (int i = 0; i < tree.getColumnCount() - 1; i++) {
columnsWidth += tree.getColumn(i).getWidth();
}
TreeColumn lastColumn = tree.getColumn(tree.getColumnCount() - 1);
lastColumn.pack();
Rectangle area = tree.getClientArea();
Point preferredSize = tree.computeSize(SWT.DEFAULT, SWT.DEFAULT);
int width = area.width - 2*tree.getBorderWidth();
if (preferredSize.y > area.height + tree.getHeaderHeight()) {
// Subtract the scrollbar width from the total column width
// if a vertical scrollbar will be required
Point vBarSize = tree.getVerticalBar().getSize();
width -= vBarSize.x;
}
// last column is packed, so that is the minimum. If more space is available, add it.
if(lastColumn.getWidth() < width - columnsWidth) {
lastColumn.setWidth(width - columnsWidth);
}
}
Works well for me - you might want to set column resizable to false ;-). This can also be called when data in the last column changes (introducting / removing vertical scroll bar).
Thanks Thomas. Your idea worked for me as well, though I was using TableViewer and TableColumn.
Quoting my code so that others can take some hints.
`public void controlResized(ControlEvent e) {
if ( listOfTableColumns.size() != colProportions.length )
{
logger.warn( "Number of columns passed and size of column proportions array are different. " +
"Columns resizing shall not be effective on GUI window resizing" );
return;
}
Rectangle area = tableBaseComposite.getClientArea();
Point size = theTable.computeSize(SWT.DEFAULT, SWT.DEFAULT);
ScrollBar vBar = theTable.getVerticalBar();
int width = area.width - theTable.computeTrim(0,0,0,0).width - vBar.getSize().x;
if (size.y > area.height + theTable.getHeaderHeight()) {
// Subtract the scrollbar width from the total column width
// if a vertical scrollbar will be required
Point vBarSize = vBar.getSize();
width -= vBarSize.x;
}
Point oldSize = theTable.getSize();
if (oldSize.x > area.width) {
// table is getting smaller so make the columns
// smaller first and then resize the table to
// match the client area width
int index = 0 ;
for ( Iterator<TableColumn> iterator = listOfTableColumns.iterator(); iterator.hasNext(); )
{
TableColumn column = iterator.next();
column.setWidth( (int) numberFromPercentage( width, colProportions[index++] ) );
}
listOfTableColumns.get( listOfTableColumns.size() - 1).pack();
theTable.setSize(area.width, area.height);
} else {
// table is getting bigger so make the table
// bigger first and then make the columns wider
// to match the client area width
int index = 0;
theTable.setSize(area.width, area.height);
for ( Iterator<TableColumn> iterator = listOfTableColumns.iterator(); iterator.hasNext(); )
{
TableColumn column = iterator.next();
column.setWidth( (int) numberFromPercentage( width, colProportions[index++] ) );
}
listOfTableColumns.get( listOfTableColumns.size() - 1).pack();
}
}`
No need for complicated hacks to remove the extra unwanted column space at the end...
Just create a columnLayout:
TableColumnLayout columnLayout = new TableColumnLayout();
and then set it to each of your columns:
columnLayout.setColumnData(YOUR_VIEWER_COLUMN1.getColumn(), new ColumnPixelData(200));
columnLayout.setColumnData(YOUR_VIEWER_COLUMN2.getColumn(), new ColumnWeightData(200, 100));
Finally, set the layout on your parent composite:
parent.setLayout(columnLayout);
Full sample:
public void createPartControl(Composite parent) {
TableViewer viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION);
TableViewerColumn keyColumn = new TableViewerColumn(viewer, SWT.LEFT);
TableViewerColumn valueColumn = new TableViewerColumn(viewer, SWT.LEFT);
TableColumnLayout columnLayout = new TableColumnLayout();
columnLayout.setColumnData(keyColumn.getColumn(), new ColumnPixelData(200));
columnLayout.setColumnData(valueColumn.getColumn(), new ColumnWeightData(200, 100));
parent.setLayout(columnLayout);
}
Just guessing: maybe your columns do not get resized to fill all the table?
How do you set the widths of columns?
Consider using TableColumnLayout for the table container.
On windows, you will always get an extra column/row if the net width of all the columns that has been set up is less than the dimension of the table. So its always good to make your columns fit your table, also there is some space left for scroll bars, though I am not very sure about this, but its always better to specify whether you want vertical or horizontal scroll bars.
I used the packAndFillLastColumn() method and it worked for me. But I found one issue with it. My table was created with a border. After using the packAndFillLastColumn() method the border for the row no longer exists. I used the setLinesVisible(true) method within the packAndFillLastColumn() method but still that does not work.
So simple! Just remove this line in your table commands inside the createContents function:
table.getColumn(i).pack();
Good-luck
As a workaround use :
-For Column
use TableColumnLayout for the treeViewer's composite and set appropriate column data for each column using:
"tableColumnLayout.setColumnData(column,new ColumnWeightData(...as per your requirement));"
-For Row
Set GridData to the treeViewer's composite and provide height hint using
"gridData.heightHint = table.getItemHeight()*numberOfVisibleRows"
I found eclipse has marked it as WONTFIX.. so can not do much to remove this space..We have tp live with it...:)
To the end column we need to set the setWidth to window size or shell-size, parent-shell size like 1500,5000
final TableViewerColumn viewerColumn = new TableViewerColumn(tableViewer, SWT.NONE);
final TableColumn column = viewerColumn.getColumn();
column.setText(title);
column.setResizable(true);
column.setMoveable(true);
//set the setWidth size upto shell size or set upto to some size like 1000,1500,2000,5000
col.setWidth(comp.getShell().getSize().x); // or col.setWidth(1500) ;
return viewerColumn;