1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "TestSceneBase.h" 18 #include "utils/Color.h" 19 20 #include <cstdio> 21 22 class ListViewAnimation; 23 24 static TestScene::Registrar _ListView(TestScene::Info{ 25 "listview", 26 "A mock ListView of scrolling content. Doesn't re-bind/re-record views as they are recycled, so" 27 "won't upload much content (either glyphs, or bitmaps).", 28 TestScene::simpleCreateScene<ListViewAnimation> 29 }); 30 31 class ListViewAnimation : public TestScene { 32 public: 33 int cardHeight; 34 int cardSpacing; 35 int cardWidth; 36 int cardLeft; 37 sp<RenderNode> listView; 38 std::vector< sp<RenderNode> > cards; 39 void createContent(int width, int height, TestCanvas& canvas) override { 40 srand(0); 41 cardHeight = dp(60); 42 cardSpacing = dp(16); 43 cardWidth = std::min((height - cardSpacing * 2), (int)dp(300)); 44 cardLeft = (width - cardWidth) / 2; 45 46 for (int y = 0; y < height + (cardHeight + cardSpacing - 1); y += (cardHeight + cardSpacing)) { 47 cards.push_back(createCard(cards.size(), y)); 48 } 49 listView = TestUtils::createNode(0, 0, width, height, 50 [this](RenderProperties& props, TestCanvas& canvas) { 51 for (size_t ci = 0; ci < cards.size(); ci++) { 52 canvas.drawRenderNode(cards[ci].get()); 53 } 54 }); 55 56 canvas.drawColor(Color::Grey_500, SkXfermode::kSrcOver_Mode); 57 canvas.drawRenderNode(listView.get()); 58 } 59 60 void doFrame(int frameNr) override { 61 int scrollPx = dp(frameNr) * 3; 62 int cardIndexOffset = scrollPx / (cardSpacing + cardHeight); 63 int pxOffset = -(scrollPx % (cardSpacing + cardHeight)); 64 65 TestCanvas canvas( 66 listView->stagingProperties().getWidth(), 67 listView->stagingProperties().getHeight()); 68 for (size_t ci = 0; ci < cards.size(); ci++) { 69 // update card position 70 auto card = cards[(ci + cardIndexOffset) % cards.size()]; 71 int top = ((int)ci) * (cardSpacing + cardHeight) + pxOffset; 72 card->mutateStagingProperties().setLeftTopRightBottom( 73 cardLeft, top, cardLeft + cardWidth, top + cardHeight); 74 card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); 75 76 // draw it to parent DisplayList 77 canvas.drawRenderNode(cards[ci].get()); 78 } 79 listView->setStagingDisplayList(canvas.finishRecording(), nullptr); 80 } 81 private: 82 SkBitmap createRandomCharIcon() { 83 int size = cardHeight - (dp(10) * 2); 84 SkBitmap bitmap = TestUtils::createSkBitmap(size, size); 85 SkCanvas canvas(bitmap); 86 canvas.clear(0); 87 88 SkPaint paint; 89 paint.setAntiAlias(true); 90 SkColor randomColor = BrightColors[rand() % BrightColorsCount]; 91 paint.setColor(randomColor); 92 canvas.drawCircle(size / 2, size / 2, size / 2, paint); 93 94 bool bgDark = SkColorGetR(randomColor) + SkColorGetG(randomColor) + SkColorGetB(randomColor) 95 < 128 * 3; 96 paint.setColor(bgDark ? Color::White : Color::Grey_700); 97 paint.setTextAlign(SkPaint::kCenter_Align); 98 paint.setTextSize(size / 2); 99 char charToShow = 'A' + (rand() % 26); 100 canvas.drawText(&charToShow, 1, size / 2, /*approximate centering*/ size * 0.7, paint); 101 return bitmap; 102 } 103 104 static SkBitmap createBoxBitmap(bool filled) { 105 int size = dp(20); 106 int stroke = dp(2); 107 SkBitmap bitmap = TestUtils::createSkBitmap(size, size); 108 SkCanvas canvas(bitmap); 109 canvas.clear(Color::Transparent); 110 111 SkPaint paint; 112 paint.setAntiAlias(true); 113 paint.setColor(filled ? Color::Yellow_500 : Color::Grey_700); 114 paint.setStyle(filled ? SkPaint::kStrokeAndFill_Style : SkPaint::kStroke_Style); 115 paint.setStrokeWidth(stroke); 116 canvas.drawRect(SkRect::MakeLTRB(stroke, stroke, size - stroke, size - stroke), paint); 117 return bitmap; 118 } 119 120 sp<RenderNode> createCard(int cardId, int top) { 121 return TestUtils::createNode(cardLeft, top, cardLeft + cardWidth, top + cardHeight, 122 [this, cardId](RenderProperties& props, TestCanvas& canvas) { 123 static SkBitmap filledBox = createBoxBitmap(true); 124 static SkBitmap strokedBox = createBoxBitmap(false); 125 126 // TODO: switch to using round rect clipping, once merging correctly handles that 127 SkPaint roundRectPaint; 128 roundRectPaint.setAntiAlias(true); 129 roundRectPaint.setColor(Color::White); 130 canvas.drawRoundRect(0, 0, cardWidth, cardHeight, dp(6), dp(6), roundRectPaint); 131 132 SkPaint textPaint; 133 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 134 textPaint.setColor(rand() % 2 ? Color::Black : Color::Grey_500); 135 textPaint.setTextSize(dp(20)); 136 textPaint.setAntiAlias(true); 137 char buf[256]; 138 snprintf(buf, sizeof(buf), "This card is #%d", cardId); 139 TestUtils::drawUtf8ToCanvas(&canvas, buf, textPaint, cardHeight, dp(25)); 140 textPaint.setTextSize(dp(15)); 141 TestUtils::drawUtf8ToCanvas(&canvas, "This is some more text on the card", textPaint, 142 cardHeight, dp(45)); 143 144 canvas.drawBitmap(createRandomCharIcon(), dp(10), dp(10), nullptr); 145 146 const SkBitmap& boxBitmap = rand() % 2 ? filledBox : strokedBox; 147 canvas.drawBitmap(boxBitmap, cardWidth - dp(10) - boxBitmap.width(), dp(10), nullptr); 148 }); 149 } 150 }; 151