Skip to content

Commit

Permalink
feat: android styling and conditionals (#103)
Browse files Browse the repository at this point in the history
* feat: android styling and conditionals

* feat: text wrapping

* chore: rebase from alpha

* fix: resolve merge conflicts

Co-authored-by: Vittorio Cellucci <vel@qlik.com>
  • Loading branch information
vcellu and vcellu committed Nov 2, 2022
1 parent 69f74a6 commit 327d091
Show file tree
Hide file tree
Showing 29 changed files with 1,010 additions and 214 deletions.
@@ -0,0 +1,11 @@
package com.qliktrialreactnativestraighttable;

import com.facebook.react.bridge.ReadableMap;

public class CellContentStyle extends HeaderContentStyle {
int rowHeight = 1;
CellContentStyle(ReadableMap data) {
super(data);
rowHeight = JsonUtils.getInt(data, "rowHeight", 1);
}
}
Expand Up @@ -30,17 +30,19 @@ public class CellView extends LinearLayout implements SelectionsObserver {

CellView(Context context, String type, SelectionsEngine selectionsEngine, TableView tableView, boolean firstColumn) {
super(context);
this.setPadding(padding, 0, padding, 0);
this.tableView = tableView;
if(type.equals("text")) {
content = new ClickableTextView(context, selectionsEngine, tableView, this);
ClickableTextView textView = new ClickableTextView(context, selectionsEngine, tableView, this);
textView.setPadding(padding, 0, padding, 0);
content = textView;
} else if(type.equals("image")) {
content = new ClickableImageView(context, selectionsEngine, tableView, this);
} else if(type.equals("miniChart")) {
content = new MiniChartView(context);
this.setPadding(padding, 0, padding, 0);
}

this.selectionsEngine = selectionsEngine;
this.tableView = tableView;
this.firstColumn = firstColumn;
this.dragBoxEventHandler = tableView.dragBoxEventHandler;

Expand Down Expand Up @@ -156,6 +158,7 @@ public boolean onSingleTapConfirmed(MotionEvent motionEvent) {
handleSingleTap();
return true;
}

@Override
public void onLongPress(MotionEvent e) {
((View) content).showContextMenu();
Expand Down
Expand Up @@ -42,8 +42,8 @@ public boolean onTouchEvent(MotionEvent e) {
private void alwaysFit() {
ViewGroup parent = (ViewGroup) cellView.getParent();
ViewGroup.LayoutParams layout = parent.getLayoutParams();
layout.height = TableTheme.rowHeight;
layout.width = TableTheme.rowHeight + parent.getPaddingLeft() + parent.getPaddingRight();
layout.height = tableView.rowHeight;
layout.width = tableView.rowHeight + parent.getPaddingLeft() + parent.getPaddingRight();
parent.setLayoutParams(layout);
this.setScaleType(ScaleType.FIT_CENTER);
scaleType = "alwaysFit";
Expand All @@ -52,8 +52,8 @@ private void alwaysFit() {
private void stretchToFit(DataColumn column) {
ViewGroup parent = (ViewGroup) cellView.getParent();
ViewGroup.LayoutParams layout = parent.getLayoutParams();
layout.height = TableTheme.rowHeight;
layout.width = (int)column.width;
layout.height = tableView.rowHeight;
layout.width = column.width;
parent.setLayoutParams(layout);
this.setScaleType(ScaleType.FIT_XY);
scaleType = "stretchToFit";
Expand All @@ -67,8 +67,8 @@ private void fitToHeight(Bitmap image) {

ViewGroup parent = (ViewGroup) cellView.getParent();
ViewGroup.LayoutParams layout = parent.getLayoutParams();
layout.height = TableTheme.rowHeight;
layout.width = Math.round(TableTheme.rowHeight * aspectRatioMultiplier) + parent.getPaddingLeft() + parent.getPaddingRight();
layout.height = tableView.rowHeight;
layout.width = Math.round(tableView.rowHeight * aspectRatioMultiplier) + parent.getPaddingLeft() + parent.getPaddingRight();
parent.setLayoutParams(layout);
this.setScaleType(ScaleType.FIT_XY);
scaleType = "fitToHeight";
Expand All @@ -88,7 +88,7 @@ private void fitToWidth(DataColumn column, Bitmap image) {

ViewGroup grandparent = (ViewGroup) parent.getParent();
ViewGroup.LayoutParams grandparentLayout = grandparent.getLayoutParams();
grandparentLayout.width = (int)column.width;
grandparentLayout.width = column.width;
grandparentLayout.height = Math.round(column.width * aspectRatioMultiplier);
grandparent.setLayoutParams(grandparentLayout);
this.setScaleType(ScaleType.FIT_XY);
Expand Down Expand Up @@ -130,7 +130,7 @@ public void setAlignment(DataColumn column) {
case "centerCenter":
wrapper.setGravity(Gravity.CENTER);
if (scaleType.equals("fitToWidth")) {
setTranslationY(TableTheme.rowHeight / 2 - container.getMinimumHeight() / 2);
setTranslationY(tableView.rowHeight / 2 - container.getMinimumHeight() / 2);
}
break;
case "centerLeft":
Expand All @@ -143,7 +143,7 @@ public void setAlignment(DataColumn column) {
case "centerRight":
if (scaleType.equals("fitToWidth")) {
wrapper.setGravity(Gravity.BOTTOM);
setTranslationY(TableTheme.rowHeight - container.getMinimumHeight());
setTranslationY(tableView.rowHeight - container.getMinimumHeight());
break;
}
wrapper.setGravity(Gravity.CENTER);
Expand Down
Expand Up @@ -5,6 +5,17 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.Log;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.fonts.Font;
import android.graphics.fonts.FontFamily;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.method.LinkMovementMethod;
import android.text.method.MovementMethod;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ViewGroup;
Expand All @@ -21,26 +32,63 @@ public class ClickableTextView extends androidx.appcompat.widget.AppCompatTextVi
GestureDetector gestureDetector;
final TableView tableView;
Animation fadeIn;
ClickableTextWrapper textWrapper;
ClickableTextView(Context context, SelectionsEngine selectionsEngine, TableView tableView, CellView cellView) {
super(context);
this.tableView = tableView;
this.selectionsEngine = selectionsEngine;
this.cellView = cellView;
defaultTextColor = getCurrentTextColor();
fadeIn = AnimationUtils.loadAnimation(context, R.anim.catalyst_fade_in);
textWrapper = new ClickableTextWrapper(tableView, this);
}

@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent e) {
// Checks to see if there's a link, if there is and the user
// tapped on the link text, then forward the event to the movementMethod.
// otherwise forward the event to the gesture detector
MovementMethod movementMethod = this.getMovementMethod();
if(movementMethod != null ) {
// first check to see if use tapped on text if this is a link
Rect bounds = getMeasuredTextBounds();
int x = (int)e.getX();
int y = (int)e.getY();
boolean insideText = bounds.contains(x, y);
// if user is inside, then forward to the link listener
if(insideText) {
return movementMethod.onTouchEvent(this, new SpannableString(this.getText()), e);
}
}
gestureDetector.onTouchEvent(e);
return true;
}

private Rect getMeasuredTextBounds() {
String s = this.getText().toString();

Rect bounds = new Rect();
TextPaint textPaint = this.getPaint();

textPaint.getTextBounds(s, 0, s.length(), bounds);
int baseline = this.getBaseline();
bounds.top = baseline + bounds.top;
bounds.bottom = bounds.top + this.getMeasuredHeight() ;
int startPadding = this.getPaddingStart();
bounds.left += startPadding;

bounds.right = (int) textPaint.measureText(s, 0, s.length()) + startPadding;
return bounds;
}

public void updateBackgroundColor() {
int color = selected ? TableTheme.selectedBackground : Color.TRANSPARENT;
int textColor = selected ? Color.WHITE : defaultTextColor;
int bgColor = cell.cellBackgroundColorValid ? cell.cellBackgroundColor : Color.TRANSPARENT;
int fgColor = cell.cellForegroundColorValid ? cell.cellForegroundColor : tableView.cellContentStyle.color;
int color = selected ? TableTheme.selectedBackground : bgColor ;
int textColor = selected ? Color.WHITE : fgColor ;
cellView.setBackgroundColor(color);
setBackgroundColor(color);
setTextColor(textColor);
postInvalidate();
startAnimation(fadeIn);
Expand All @@ -59,6 +107,40 @@ public void toggleSelected() {
@Override
public void setCell(DataCell cell) {
this.cell = cell;
if(cell.indicator != null) {
buildSpannableText();
} else {
setTextColor(cell.cellForegroundColorValid ? cell.cellForegroundColor : tableView.cellContentStyle.color);
setBackgroundColor(cell.cellBackgroundColorValid ? cell.cellBackgroundColor : Color.TRANSPARENT);
setText(cell.qText);
textWrapper.countWords(cell.qText);
}
}

private void buildSpannableText() {
int textColor = cell.indicator.applySegmentColors ? cell.indicator.color : TableTheme.defaultTextColor;
StringBuilder builder = new StringBuilder(cell.qText);
Spannable spannable;
if(cell.indicator.hasIcon) {
if(cell.indicator.position.equals("right")) {
builder.append(" ");
builder.append(cell.indicator.icon);
spannable = new SpannableString(builder.toString());
spannable.setSpan(new ConditionalTypeFaceSpan(this.getTypeface(), textColor), 0, cell.qText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
spannable.setSpan(new ConditionalTypeFaceSpan(TableTheme.iconFonts, cell.indicator.color), cell.qText.length(), cell.qText.length() + 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} else {
builder.insert(0, cell.indicator.icon);
builder.insert(1, " ");
spannable = new SpannableString(builder.toString());
spannable.setSpan(new ConditionalTypeFaceSpan(TableTheme.iconFonts, cell.indicator.color), 0, 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
spannable.setSpan(new ConditionalTypeFaceSpan(this.getTypeface(), textColor), 2, cell.qText.length() + 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
} else {
spannable = new SpannableString(builder.toString());
spannable.setSpan(new ConditionalTypeFaceSpan(this.getTypeface(), textColor), 0, cell.qText.length() , Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
setText(spannable);
textWrapper.countWords(spannable.toString());
}

@Override
Expand All @@ -74,4 +156,24 @@ public void setSelected(boolean value) {
public boolean isSelected(){
return selected;
}

@Override
public void setMaxLines(int maxLines) {
maxLines = textWrapper.setMaxLines(maxLines);
super.setMaxLines(maxLines);
}

public void testTextWrap(DataColumn dataColumn) {
if(tableView.cellContentStyle.wrap) {
textWrapper.testTextWrap(dataColumn);
}
}

public int getMeasuredLineCount() {
return textWrapper.getMeasuredLinedCount();
}

public int measureLines(DataColumn dataColumn) {
return textWrapper.getMeasureLinedCount(dataColumn);
}
}
@@ -0,0 +1,27 @@
package com.qliktrialreactnativestraighttable;

import android.widget.TextView;

public class ClickableTextWrapper extends TextWrapper{
ClickableTextWrapper(TableView tableView, TextView textView) {
super(tableView, textView);
}

@Override
void measureLineCount() {
if(wordCount > 1) {
int lines = calculateLineCount();
if(lines != lineCount && lines <= wordCount) {
lineCount = lines;
tableView.updateRecyclerViewLineCount(column);
}
}
}
int getMeasureLinedCount(DataColumn column) {
this.column = column;
if(wordCount > 1) {
return calculateLineCount();
}
return 1;
}
}
Expand Up @@ -68,6 +68,9 @@ private boolean loadWidthsFromStorage() {
JSONArray jsonArray = new JSONArray(value);
for(int i = 0; i< jsonArray.length(); i++) {
JSONArray jsonWidths = jsonArray.getJSONArray(i);
if(jsonWidths.length() != dataColumns.size()) {
return false;
}
for(int j = 0; j < jsonWidths.length(); j++) {
dataColumns.get(j).width = jsonWidths.getInt(j);
}
Expand Down
@@ -0,0 +1,65 @@
package com.qliktrialreactnativestraighttable;

import android.annotation.SuppressLint;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.text.TextPaint;
import android.text.style.TypefaceSpan;

import androidx.annotation.NonNull;

public class ConditionalTypeFaceSpan extends TypefaceSpan {
private Typeface newType;
private int textColor = TableTheme.defaultTextColor;

public ConditionalTypeFaceSpan(Typeface typeface) {
super(typeface);
newType = typeface;
}
public ConditionalTypeFaceSpan(Typeface typeface, int textColor) {
super(typeface);
newType = typeface;
this.textColor = textColor;
}

public ConditionalTypeFaceSpan(String family, Typeface typeface) {
super(family);
newType = typeface;
}

public ConditionalTypeFaceSpan(String family, Typeface typeface, int color) {
super(family);
newType = typeface;
textColor = color;
}

@Override
public void updateDrawState(TextPaint tp) {
apply(tp, newType );
}

@Override
public void updateMeasureState(@NonNull TextPaint paint) {
apply(paint, newType);
}

@SuppressLint("WrongConstant")
private void apply(Paint paint, Typeface tp) {
int oldStyle;
Typeface old = paint.getTypeface();
if (old == null) {
oldStyle = 0;
} else {
oldStyle = old.getStyle();
}

int fake = oldStyle & ~tp.getStyle();
if ((fake & Typeface.BOLD) != 0) {
paint.setFakeBoldText(true);
}

paint.setColor(textColor);
paint.setTypeface(tp);

}
}
@@ -1,6 +1,7 @@
package com.qliktrialreactnativestraighttable;

import android.view.GestureDetector;
import android.view.MotionEvent;

public interface Content {
void updateBackgroundColor();
Expand Down
@@ -0,0 +1,25 @@
package com.qliktrialreactnativestraighttable;

import android.content.Context;

import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

public class CustomLinearLayoutManger extends LinearLayoutManager {
boolean initialized = false;
CustomRecyclerView recyclerView = null;
CustomLinearLayoutManger(Context context) {
super(context);
}

@Override
public void onLayoutCompleted(RecyclerView.State state) {
super.onLayoutCompleted(state);
if(!initialized && recyclerView != null) {
if(recyclerView.testTextWrap()) {
recyclerView = null;
initialized = true;
}
}
}
}

0 comments on commit 327d091

Please sign in to comment.