I'm trying to create a table in which each of the columns is as wide as the widest textview.
The first row I'm trying to create will be filled with names.No matter what I try I cannot seem to get all the columns to be the same width. Please note that I do not want to stretch the columns to the width of the screen, if I have to scroll horizontally, that's okay.
So basically if the widest name is 100dp wide, I want every column to be that wide. I figured by setting the weight in my TextViews I would get evenly spaced columns, but that does not seem to be the case.
Here is the code I have so far to create the first row in my table.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detailed_scores);
game = (Game) getIntent().getExtras().getSerializable("game");
TableLayout detailedScoreLayout = findViewById(R.id.detailedScoreLayoutId);
// First create the row with all the player names.
detailedScoreLayout.addView(createNameRow());
}
}
private TableRow createNameRow() {
TableRow row = new TableRow(this);
for (GamePlayer player: game.getPlayers()) {
row.addView(createName(player.getName()));
row.addView(createVerticalDivider());
}
return row;
}
private TextView createName(String name) {
TextView nameText = new TextView(this);
nameText.setText(name);
formatText(nameText, 24);
return nameText;
}
private void formatText(TextView text, int textSize) {
TableRow.LayoutParams params = new TableRow.LayoutParams(
TableRow.LayoutParams.WRAP_CONTENT,
TableRow.LayoutParams.MATCH_PARENT);
int margin = intToDp(12);
int halfMargin = intToDp(6);
params.setMargins(margin, halfMargin, margin, halfMargin);
text.setLayoutParams(params);
text.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize);
text.setTextColor(Color.BLACK);
text.setGravity(Gravity.CENTER);
text.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
}
private View createVerticalDivider() {
View div = new View(this);
div.setLayoutParams(new TableRow.LayoutParams(intToDp(1), TableRow.LayoutParams.MATCH_PARENT));
div.setBackgroundColor(Color.BLACK);
return div;
}
Any idea what I'm missing here?
why don't you try to use a library like
https://github.com/evrencoskun/TableView.
it is easy to customize and use.
Related
I have a vertical LinearLayout with a height of 32dp and i want to fit two TextView of 16dp each. The length of the text is different and i want it to always nicely fit in a single line.
I have tried with an AutoTextView and with a classic TextView + AutoSizing by TextViewCompat but none of these really work. My text shrink a bit but always finish to be crop at the bottom.
Here the base code
LinearLayout adressTextLayout = new LinearLayout(activity);
adressTextLayout.setOrientation(LinearLayout.VERTICAL);
adressTextLayout.setLayoutParams(new TableLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
//icon_size_big = 32dp
(int) (activity.getResources().getDimension(R.dimen.icon_size_big))));
boolean noAdress = true;
//First line with Country, Region and department
if(!mondialAdress.isEmpty()){
noAdress = false;
TextView tx = Utils.createAutoFitTextView(activity, mondialAdress, true, (int) (activity.getResources().getDimension(R.dimen.icon_size_big)/2));
tx.setTypeface(null, Typeface.ITALIC);
adressTextLayout.addView(tx);
}
//Second line with adress, postal code and city
if(!localAdress.isEmpty()){
noAdress = false;
TextView tx = Utils.createAutoFitTextView(activity, localAdress, true, (int) (activity.getResources().getDimension(R.dimen.icon_size_big)/2));
adressTextLayout.addView(tx);
}
//Default line in case we didn't found any adress
if(noAdress){
adressTextLayout.addView(Utils.createAutoFitTextView(activity, activity.getResources().getString(R.string.hint_provide_information), true, 0));
}
Here is the createAutoFitTextView() function with TextViewCompat
public static TextView createAutoFitTextView(Activity activity, String text, boolean singleLine, int maxHeight) {
TextView textView = new AutofitTextView(activity);
textView.setText(text);
//MATCH_PARENT by default
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, maxHeight > 0 ? maxHeight : ViewGroup.LayoutParams.MATCH_PARENT);
textView.setLayoutParams(params);
//Auto Sizing by TextViewCompat
TextViewCompat.setAutoSizeTextTypeWithDefaults(textView, TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
if(singleLine){
textView.setSingleLine(true);
}
return textView;
}
Here the function with AutoFitTextView
public static AutofitTextView createAutoFitTextView(Activity activity, String text, boolean singleLine, int maxHeight) {
AutofitTextView textView = new AutofitTextView(activity);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, maxHeight != -1 ? maxHeight : ViewGroup.LayoutParams.MATCH_PARENT);
textView.setLayoutParams(params);
textView.setText(text);
if(singleLine){
textView.setSingleLine(true);
}
return textView;
}
Here a the result :
With TextViewCompat
With AutoFillTextView:
I have a paging algorithm for the RecyclerView, which if done Scroll, a scrolling listener triggers and loads more elements. Initially, I assigned a number of elements to load in the request to the server by default that is 20 for my RecyclerView, which has a fixed size (not wrap_content).
I need to know the number of items that can be visible in the width/height of the defined RecyclerView before loading the data, to determine the amount of items to be requested in the initial load, given that with 20 items on some devices is not enough to activate the listener of the scroll and load more elements.
This is without considering the extra properties of the view, such as padding, margin etc ...
The solution can be in Java Android or Xamarin Android (not Forms) C#.
Update:
For you to have a clue, i have tried this and it works for me, only if I call it when the size of RecyclerView is assigned, inside the OnLayoutChange:
public int GetMaxVisibleItemCountFromRecyclerView(RecyclerView recyclerView)
{
if (recyclerView == null) return 0;
int Width = recyclerView.Width;
int Height = recyclerView.Height;
if (Width == 0 || Height == 0)
return 0;
var layoutManager = recyclerView.GetLayoutManager() as GridLayoutManager;
if (layoutManager == null) return 0;
int widthRatio = Width / layoutManager.SpanCount;
int quantity = (Height / widthRatio) * layoutManager.SpanCount;
return quantity;
}
This solution only works for RecyclerViews that use the GridLayoutManager. I have other RecyclerViews with defined sizes that also use the paging algorithm with a LinearLayoutManager.
I need a similar solution, that works with any LayoutManager of the RecyclerView and does not have to be called inside the OnLayoutChange, is this possible?
Instead of trying to replicate the computations that RecyclerView does to compute layout, let the system do the work for you. The following example lets RecyclerView lay out one item and measurements are taken from that. The dummy item is not displayed and is used just for measurement.
The advantage of this method is that we don't have to replicate what RecyclerView does to measure items. All key measurements are taken into account including padding, margins and decorations.
The following sample shows how this can be accomplished for GridLayoutManager and LinearLayoutManager. StaggeredGridLayoutManager and FlexboxLayoutManager are special cases and aren't taken into account here.
Here is a short video showing the results of this demo app showing that just one page of items was loaded.
MainActivity.java
public class MainActivity extends AppCompatActivity {
private List<String> mItems = new ArrayList<>();
private RecyclerView mRecycler;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecycler = findViewById(R.id.recyclerView);
// Sample for vertical LinearLayoutManager.
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
// Sample for GridLayoutManager with 4 spans. Each item comsumes 2 spans.
// GridLayoutManager layoutManager = new GridLayoutManager(this, 4);
// layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
// #Override
// public int getSpanSize(int position) {
// return 2;
// }
// });
// Add single dummy item that will be measured but not be displayed.
mItems.add("Dummy item");
RecyclerViewAdapter mAdapter = new RecyclerViewAdapter(mItems);
mRecycler.setLayoutManager(layoutManager);
mRecycler.setAdapter(mAdapter);
// Take measurements in OnPreDraawListener(). This could also be accomplished with
// mRecyclerView.post(new Runnable()...)
mRecycler.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
mRecycler.getViewTreeObserver().removeOnPreDrawListener(this);
// RecyclerView is laid out with single dummy entry. Get how many of this type
// of item can fit into the visible portion of the RecyclerView.
final int nItems = getInitialLoadCount(mRecycler);
Log.d(TAG, "<<<<Items per page=" + nItems);
// Don't need the dummy entry any more.
mItems.clear();
mRecycler.getAdapter().notifyDataSetChanged();
mItems = new ArrayList<>();
// Fake load...
loadInitialItems(nItems);
return false;
}
});
}
// Determine how many items will fill one screen of the RecyclerView. Call with the
// RecyclerView loaded with at least one item for measurement.
private int getInitialLoadCount(RecyclerView recyclerView) {
int itemsToLoad = 0;
RecyclerView.LayoutManager lm = recyclerView.getLayoutManager();
View firstChild = recyclerView.getChildAt(0);
if (lm instanceof LinearLayoutManager) {
Rect bounds = new Rect();
recyclerView.getDecoratedBoundsWithMargins(firstChild, bounds);
if (lm.canScrollVertically()) {
int recyclerHeightForItems = recyclerView.getHeight() - recyclerView.getPaddingTop()
- recyclerView.getPaddingBottom();
itemsToLoad = (recyclerHeightForItems + bounds.height() - 1) / bounds.height();
} else if (lm.canScrollHorizontally()) {
int recyclerWidthForItems = recyclerView.getWidth() - recyclerView.getPaddingLeft()
- recyclerView.getPaddingRight();
itemsToLoad = (recyclerWidthForItems + bounds.width() - 1) / bounds.width();
}
if (lm instanceof GridLayoutManager) {
// Adjust for GridLayoutManager. All items should to be the same number of spans.
GridLayoutManager glm = (GridLayoutManager) lm;
itemsToLoad *= glm.getSpanCount() / glm.getSpanSizeLookup().getSpanSize(0);
}
}
return itemsToLoad;
}
private void loadInitialItems(final int itemCount) {
// Simulate load of nItems...should be on non-UI thread.
new Thread(new Runnable() {
#Override
public void run() {
for (int i = 1; i <= itemCount; i++) {
sleep(250);
mItems.add("Item #" + i);
}
runOnUiThread(new Runnable() {
#Override
public void run() {
mRecycler.swapAdapter(new RecyclerViewAdapter(mItems), true);
}
});
}
}).start();
}
private static final String TAG = "MainActivity";
}
If your RecyclerView and your item width and height are defined in dp, then you should save their dimensions in dimen.xml. Then you can calculate how many items will fit like this:
float recyclerHeight = getResources().getDimension(R.dimen.recycler_height);
float itemHeight = getResources().getDimension(R.dimen.item_height);
int numOfItemsFit = (int) (recyclerHeight / itemHeight);
If your recyclerView isn't defined by dp, but the rest of the views in this layout are you can try to accomplish the same thing by decreasing the other views height from the total view height. You can check the total view height with:
public int getHeight() {
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
return size.y;
}
If you can't do the above you'll need to measure your RecyclerView inside of OnLayoutChange :( . You can do that with: recyclerView.getMeasuredHeight(); and recyclerView.getMeasuredWidth();
Im trying to make my listview update the colour of the "android.R.id.text1" line (top line of the two listview lines) whenever you click the "Enrollment" button. The class also calls colourCodeCourses() on create and that works fine to correctly display the course colours, however when I click the enrollment button no matter what I've tried I can't get my listview to update the colour of the text until I hit "back" and reopen the Activities view. At that point, the listview appears in the correct colour. (These aren't all the methods and instance variables, just the relevant ones). Any help is appreciated.
private ListView listView;
private ArrayAdapter<Course> courseArrayAdapter;
private AccessEnrollment accessEnroll;
private ArrayList<Course> comparisonList;
private Degree degree;
public void buttonEnrollOnClick(View v){
int position = listView.getCheckedItemPosition();
Course course = (Course)listView.getItemAtPosition(position);
TextView courseID = (TextView)listView.findViewById(android.R.id.text1);
TextView courseName = (TextView)listView.findViewById(android.R.id.text2);
accessEnroll.insertEnrollmentData(course);
degree.addCourse(course);
colourCodeCourses(position, courseID, courseName, true);
courseArrayAdapter.notifyDataSetChanged();
}
private void colourCodeCourses(int position, TextView courseTitle, TextView courseDescription, boolean newEnroll) {
displayGrade = "";
if(!newEnroll){
if (degree.getCourse(position).getGrade()!="N")
{
displayGrade = " {" + degree.getCourse(position).getGrade() + "} ";
}
courseTitle.setText(degree.getCourse(position).getCourseID()+ " " + displayGrade );
courseDescription.setText(degree.getCourse(position).getCourseName());
if (comparisonList.contains(degree.getCourse(position)))
{
if (degree.getCourse(position).getGrade().equals("IP")){
courseTitle.setTextColor(Color.rgb(0,0,160));
}else{
courseTitle.setTextColor(Color.rgb(0, 160, 0));
}
}
else
{
courseTitle.setTextColor(Color.rgb(160, 0, 0));
}
}else{
courseTitle.setTextColor(Color.BLUE);
}
}
You have to set your styles in ListView Adapter's getView() method.
As far as I can see now you are just searching for a TextView inside a ListView and set its text color.
You should store selected state in your Adapter and set the color of your TextView's accordingly.
Here is the link to go: http://www.vogella.com/tutorials/AndroidListView/article.html
I have a LinearLayout which I've looped a number of new Button objects into. How do I go about clearing that div correctly (eg removing all the buttons)? I've tried a number of times (unsuccessfully) to do this, but have nothing to show for it.
** edit **
I'm not sure if this helps, but in flex/AS3 I would do something like:
while(myView.numChildren) myView.removeChildAt(0);
** a little code **
View col1 = findViewById(R.id.col1);
for(final Map.Entry<String,HashMap<String,String>> entry : _nav.entrySet()) {
Button item = new Button(this);
item.setText(entry.getKey());
item.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
openCol2(entry);
}
});
((LinearLayout) col1).addView(item);
}
private final void openCol2(Map.Entry<String,HashMap<String,String>> entry) {
View col2 = findViewById(R.id.col2);
// here is where I want to clean out col2. Right before I add more buttons.
for(int i = 0; i < _nav.size(); ++i) {
Button item = new Button(this);
//item.setText(entry.getKey());
((LinearLayout) col2).addView(item);
}
}
Try this
LinearLayout col2 = (LinearLayout)findViewById(R.id.col2);
col2.removeAllViews();
Assumption: R.id.col2 is of LinearLayout type else to make it more generic typecast it to ViewGroup. Hope this help!!!
I'm trying to create something similar to the home screen layout. This consists of multiple vertically oriented LinearLayouts within a single horizontally oriented LinearLayout, all housed in a HorizontalScrollView. I've written it as a custom class called 'HomeLayout' extending HorizontalScrollView.
LinearLayout wrapper = new LinearLayout(getContext());
LayoutParams params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
wrapper.setLayoutParams(params);
wrapper.setOrientation(LinearLayout.HORIZONTAL);
addView(wrapper);
for(int i = 0; i < 2; i++) {
LinearLayout linear = (LinearLayout) View.inflate(getContext(), R.layout.screens, null);
linear.setLayoutParams(params);
wrapper.addView(linear);
}
The problem is 'screen2' has width '0' when added.
This only works if I call this in onDraw() and use getMeasuredWidth() and getMeasuredHeight() instead of LayoutParams.FILL_PARENT. And even then, I'm not sure if that's the right thing to do.
Furthermore I'm unable to reference the views 'wrapper', 'screen1', 'screen2' in onCreate().
I loosely followed this link: http://blog.velir.com/index.php/2010/11/17/android-snapping-horizontal-scroll/
What am I doing wrong?
Hoping this helps others. I solved my problem by implementing onMeasure() in the following way:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(width, height);
for(int i = 0; i < mWrapper.getChildCount(); i++) {
View child = mWrapper.getChildAt(i);
child.setLayoutParams(new LinearLayout.LayoutParams(width,
LayoutParams.FILL_PARENT));
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}