Этот вопрос относится только к кодовому имени 1.
Вариант использования
Некоторые приложения, такие как Instagram, имеют прокручиваемые поля по оси X, как в этом видео:
https://www.informatica -libera.net / videoLavoro / Video-2019-02-07-11-16-59_0569.mp4
Этокажется довольно простым для реализации (с BoxLayout.x()
, установленным как прокручиваемый по оси X), но это не так просто.Существует скрытая сложность: ширина каждого блока указывается в процентах от ширины экрана, потому что пользователь должен увидеть первый блок и небольшой фрагмент второго блока, чтобы понять, что прокрутка возможна.Может быть, это недостаточно ясно в приложении Instagram, но это более очевидно в других приложениях.
Что я сделал
Я не смог понять, какдля размещения раскладок Codename One в соответствии с требованием x-scrollable BoxLayout.x
, в котором каждый Component
должен занимать 60% ширины экрана.Однако мне удалось получить что-то очень похожее с пользовательским Layout
, но с большой проблемой: я не нашел способа автоматически рассчитать высоту блоков в соответствии с их содержимым.На данный момент у меня есть процентная ширина и фиксированная высота.Пожалуйста, посмотрите это видео, снятое в симуляторе:
https://www.informatica -libera.net / videoLavoro / Video-2019-02-07-11-38-10_0570.mp4
Другая проблема моего подхода заключается в том, что мой код не работает с SpanLabel
(я разбил текст на токены, и для каждого токена я создал Label
).
Мой код
Следующий код представляет собой контрольный пример , который можно легко скопировать и запустить.Обратите внимание, что фактические вопросы генерируются в соответствии с пользовательскими данными, поэтому я не знаю, сколько вопросов заранее.Кроме того, ширина экрана планшета отличается от ширины экрана смартфона (поэтому высота в этих двух случаях должна быть разной).На данный момент я установил фиксированную высоту 20 мм.
TestBoxes.java
import static com.codename1.ui.CN.*;
import com.codename1.ui.Form;
import com.codename1.ui.Dialog;
import com.codename1.ui.Label;
import com.codename1.ui.plaf.UIManager;
import com.codename1.ui.util.Resources;
import com.codename1.io.Log;
import com.codename1.ui.Button;
import com.codename1.ui.Component;
import com.codename1.ui.Container;
import com.codename1.ui.Display;
import com.codename1.ui.Toolbar;
import com.codename1.ui.geom.Dimension;
import com.codename1.ui.layouts.BoxLayout;
import com.codename1.ui.layouts.FlowLayout;
import com.codename1.ui.layouts.Layout;
import com.codename1.util.StringUtil;
import java.util.LinkedHashMap;
import java.util.List;
/**
* This file was generated by <a href="https://www.codenameone.com/">Codename
* One</a> for the purpose of building native mobile applications using Java.
*/
public class TestBoxes {
private Form current;
private Resources theme;
public void init(Object context) {
// use two network threads instead of one
updateNetworkThreadCount(2);
theme = UIManager.initFirstTheme("/theme");
// Enable Toolbar on all Forms by default
Toolbar.setGlobalToolbar(true);
// Pro only feature
Log.bindCrashProtection(true);
addNetworkErrorListener(err -> {
// prevent the event from propagating
err.consume();
if (err.getError() != null) {
Log.e(err.getError());
}
Log.sendLogAsync();
Dialog.show("Connection Error", "There was a networking error in the connection to " + err.getConnectionRequest().getUrl(), "OK", null);
});
}
public void start() {
if (current != null) {
current.show();
return;
}
Form hi = new Form("Test Boxes", BoxLayout.y());
hi.add(getCompleteProfileCnt());
hi.show();
}
public void stop() {
current = getCurrentForm();
if (current instanceof Dialog) {
((Dialog) current).dispose();
current = getCurrentForm();
}
}
public void destroy() {
}
public Container getCompleteProfileCnt() {
LinkedHashMap<String, Form> questions = new LinkedHashMap<>(6);
int count = getRemainingQuestions(questions);
Label completeProfileLabel = new Label("Complete your profile");
Container boxQuestions = new Container(new BoxLayout(BoxLayout.X_AXIS_NO_GROW));
boxQuestions.setScrollableX(true);
for (String question : questions.keySet()) {
Button button = new Button("Complete");
if (questions.get(question) != null) {
button.addActionListener(l -> {
questions.get(question).show();
});
} else {
button.addActionListener(l -> {
Log.p("To be implemented...");
});
}
Container boxSingleQuestion = new Container(new FixedBoxLayout(60, 20), "ProfileUtilities-BoxSingleQuestion");
Container questionCnt = FlowLayout.encloseCenter(getArrayLabels(question, "Label"));
Container singleQuestionCnt = BoxLayout.encloseYBottomLast(questionCnt, FlowLayout.encloseCenter(button));
questionCnt.setUIID("ProfileUtilities-QuestionCnt");
singleQuestionCnt.setUIID("ProfileUtilities-SingleQuestionCnt");
boxSingleQuestion.add(singleQuestionCnt);
boxQuestions.add(boxSingleQuestion);
}
Container resultCnt = new Container(BoxLayout.y());
resultCnt.add(completeProfileLabel);
resultCnt.add(boxQuestions);
return resultCnt;
}
/**
* Inserts in the given "questions" Map the remaining questions to complete
* the profile of the current logged user, and returns the number of all
* questions (that can be >= to the remaining questions);
*
* @param questions, note that the Map will be cleared before adding the
* remaining questions
*
* @return
*/
private int getRemainingQuestions(LinkedHashMap<String, Form> questions) {
// THIS METHOD IS AN EXAMPLE, the questions are generated according to the user data
int countTotalQuestions = 3;
if (questions == null) {
throw new IllegalArgumentException("ProfileUtilities.getRemainingQuestions invalid \"questions\" param, because it's null");
}
questions.clear();
questions.put("Question 1 - Suppose that this a long text, try to make it longer", null);
questions.put("Question 2 - Suppose a short text", null);
questions.put("Question 3 - Suppose a short text plus an icon", null);
return countTotalQuestions;
}
public static List<String> tokenize(String text, String separator) {
if (separator == null) {
separator = "\n";
}
return StringUtil.tokenize(text, separator);
}
/**
* Converts a string to an array of Labels, that can be placed in a
* FlowLayout: conceptually similar to RichTextView, it serves for special
* use cases (like custom layouts) where a SpanLabel doesn't work well.
*
* @param text
* @param UIID for font style, note that margin and padding will be ignored
* @return
*/
public static Label[] getArrayLabels(String text, String UIID) {
List words = tokenize(UIManager.getInstance().localize(text, text), " ");
Label[] labels = new Label[words.size()];
for (int i = 0; i < words.size(); i++) {
labels[i] = new Label(words.get(i) + " ", UIID);
labels[i].getAllStyles().setMargin(0, 0, 0, 0);
labels[i].getAllStyles().setPadding(0, 0, 0, 0);
}
return labels;
}
class FixedBoxLayout extends Layout {
private int preferredWidth;
private final int preferredHeight;
private boolean isListenerAdded = false;
private int percentageWidth;
public FixedBoxLayout(int percentageWidth, float heightMM) {
preferredWidth = Display.getInstance().getDisplayWidth() * percentageWidth / 100;
preferredHeight = Display.getInstance().convertToPixels(heightMM);
this.percentageWidth = percentageWidth;
}
@Override
public void layoutContainer(Container parent) {
Component cmp = parent.getComponentAt(0);
cmp.setWidth(preferredWidth);
cmp.setPreferredW(preferredWidth);
cmp.setHeight(preferredHeight);
cmp.setPreferredH(preferredHeight);
if (cmp instanceof Container) {
for (Component inner : ((Container) cmp).getChildrenAsList(true)) {
inner.setWidth(preferredWidth);
inner.setPreferredW(preferredWidth);
}
}
if (!isListenerAdded) {
isListenerAdded = true;
parent.getComponentForm().addSizeChangedListener(l -> {
preferredWidth = Display.getInstance().getDisplayWidth() * percentageWidth / 100;
parent.revalidate();
});
}
}
@Override
public Dimension getPreferredSize(Container parent) {
return new Dimension(preferredWidth, preferredHeight);
}
}
}
theme.css
#Constants {
includeNativeBool: true;
}
/* Default text and color */
Default, Label, TextArea, TextField {
font-family: "native:MainRegular";
font-size: 3mm;
color: black;
}
Button {
font-family: "native:MainRegular";
font-size: 3mm;
color: white;
background-color: black;
border: 0.2mm black cn1-pill-border;
padding: 0.5mm 1mm 0.5mm 1mm; /* top, right, bottom, left */
margin: 1mm;
}
Button.pressed, Button.selected {
color: black;
background-color: white;
}
Button.disabled {
color: white;
background-color: gray;
}
ProfileUtilities-completeProfileLabel {
font-family: "native:MainBold";
font-size: 3.5mm;
color: black;
margin: 1mm;
padding: 0;
margin-bottom: 0;
}
ProfileUtilities-completeProfileBox {
margin: 0;
margin-top: 1mm;
margin-bottom: 1mm;
border: 1pt #3399ff solid;
border-radius: 2mm;
}
ProfileUtilities-CompletedQuestions {
font-family: "native:MainRegular";
font-size: 3mm;
color: darkgoldenrod;
margin: 1mm;
margin-top: 0;
padding: 0;
}
ProfileUtilities-SingleQuestionLabel {
font-family: "native:MainBold";
font-size: 3mm;
color: darkslateblue;
text-align: center;
padding: 2mm;
}
ProfileUtilities-BoxSingleQuestion {
border: 1pt darkmagenta solid;
border-radius: 2mm;
/* This is the margin between boxes */
margin: 1mm;
}
ProfileUtilities-SingleQuestionCnt {
/* This is the padding inside each box */
padding: 0;
padding-top: 1mm;
padding-bottom: 1mm;
}
ProfileUtilities-QuestionCnt {
/* This is the padding of each question text */
padding: 2mm;
}
Снимок экрана этого теста:
Мой вопрос
Мне нужен код, подходящий дляэтот вариант использования, улучшающий мой существующий код (или написание нового, если мой код слишком неправильный).Спасибо