RepoPilotOpen in app →

etsy/AndroidStaggeredGrid

An Android staggered grid view which supports multiple columns with rows of varying sizes.

Concerns

Stale and unlicensed — last commit 3y ago

weakest axis
Use as dependencyConcerns

no license — legally unclear; last commit was 3y ago…

Fork & modifyConcerns

no license — can't legally use code; no tests detected…

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isConcerns

no license — can't legally use code; last commit was 3y ago…

  • 21+ active contributors
  • Stale — last commit 3y ago
  • Concentrated ownership — top contributor handles 51% of recent commits
Show all 6 evidence items →
  • No license — legally unclear to depend on
  • No CI workflows detected
  • No test directory detected
What would change the summary?
  • Use as dependency ConcernsMixed if: publish a permissive license (MIT, Apache-2.0, etc.)
  • Fork & modify ConcernsMixed if: add a LICENSE file
  • Deploy as-is ConcernsMixed if: add a LICENSE file

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Great to learn from" badge

Paste into your README — live-updates from the latest cached analysis.

RepoPilot: Great to learn from
[![RepoPilot: Great to learn from](https://repopilot.app/api/badge/etsy/androidstaggeredgrid?axis=learn)](https://repopilot.app/r/etsy/androidstaggeredgrid)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/etsy/androidstaggeredgrid on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: etsy/AndroidStaggeredGrid

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/etsy/AndroidStaggeredGrid shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

AVOID — Stale and unlicensed — last commit 3y ago

  • 21+ active contributors
  • ⚠ Stale — last commit 3y ago
  • ⚠ Concentrated ownership — top contributor handles 51% of recent commits
  • ⚠ No license — legally unclear to depend on
  • ⚠ No CI workflows detected
  • ⚠ No test directory detected

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live etsy/AndroidStaggeredGrid repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/etsy/AndroidStaggeredGrid.

What it runs against: a local clone of etsy/AndroidStaggeredGrid — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in etsy/AndroidStaggeredGrid | Confirms the artifact applies here, not a fork | | 2 | Default branch master exists | Catches branch renames | | 3 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 4 | Last commit ≤ 1155 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>etsy/AndroidStaggeredGrid</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of etsy/AndroidStaggeredGrid. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/etsy/AndroidStaggeredGrid.git
#   cd AndroidStaggeredGrid
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of etsy/AndroidStaggeredGrid and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "etsy/AndroidStaggeredGrid(\\.git)?\\b" \\
  && ok "origin remote is etsy/AndroidStaggeredGrid" \\
  || miss "origin remote is not etsy/AndroidStaggeredGrid (artifact may be from a fork)"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 4. Critical files exist
test -f "library/src/main/java/com/etsy/android/grid/StaggeredGridView.java" \\
  && ok "library/src/main/java/com/etsy/android/grid/StaggeredGridView.java" \\
  || miss "missing critical file: library/src/main/java/com/etsy/android/grid/StaggeredGridView.java"
test -f "library/src/main/java/com/etsy/android/grid/ExtendableListView.java" \\
  && ok "library/src/main/java/com/etsy/android/grid/ExtendableListView.java" \\
  || miss "missing critical file: library/src/main/java/com/etsy/android/grid/ExtendableListView.java"
test -f "library/src/main/java/com/etsy/android/grid/HeaderViewListAdapter.java" \\
  && ok "library/src/main/java/com/etsy/android/grid/HeaderViewListAdapter.java" \\
  || miss "missing critical file: library/src/main/java/com/etsy/android/grid/HeaderViewListAdapter.java"
test -f "library/src/main/java/com/etsy/android/grid/util/DynamicHeightImageView.java" \\
  && ok "library/src/main/java/com/etsy/android/grid/util/DynamicHeightImageView.java" \\
  || miss "missing critical file: library/src/main/java/com/etsy/android/grid/util/DynamicHeightImageView.java"
test -f "library/src/main/res/values/attrs.xml" \\
  && ok "library/src/main/res/values/attrs.xml" \\
  || miss "missing critical file: library/src/main/res/values/attrs.xml"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 1155 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~1125d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/etsy/AndroidStaggeredGrid"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

AndroidStaggeredGrid is a custom Android ListView-like widget that renders items in a staggered (Pinterest-style) grid with multiple columns and rows of varying heights. It solves the problem of displaying content in a masonry layout on Android versions 2.3.3+ before RecyclerView existed, supporting configurable portrait/landscape column counts, headers/footers, and position synchronization across orientation changes. Monorepo structure: library/src/main/java/com/etsy/android/grid/ contains the core StaggeredGridView.java widget plus utility classes (DynamicHeightImageView, DynamicHeightTextView), sample/ contains a full reference app with MainActivity.java, StaggeredGridActivity.java, and SampleAdapter.java demonstrating usage patterns.

👥Who it's for

Android app developers (particularly those on Etsy's engineering team historically) who needed a production-grade staggered grid layout before Google's RecyclerView + StaggeredGridLayoutManager became the standard. Most relevant to legacy codebases still targeting older Android APIs or developers learning historical Android patterns.

🌱Maturity & risk

This project is deprecated as of September 2015 and officially abandoned by Etsy. The codebase is stable and production-tested (it powered Etsy's app), but zero new features or community PRs are being accepted. It exists as read-only reference material; the recommended path forward is RecyclerView's StaggeredGridLayoutManager.

High risk for new projects: The library is unmaintained and deprecated, with no CI/CD visible in the file list and dependency on very old Android build tools (gradle 0.12.+, compileSdkVersion 19 from ~2014). Using this in a new project locks you into legacy Android patterns; Etsy itself migrated away internally. Security patches and Android compatibility fixes will never ship.

Active areas of work

Nothing. This is an archived project with no active development. The last meaningful commit was the deprecation notice added to README.md in September 2015. No issues are being triaged, no PRs are being reviewed, and the codebase is frozen.

🚀Get running

git clone https://github.com/etsy/AndroidStaggeredGrid.git
cd AndroidStaggeredGrid
./gradlew build
./gradlew :sample:assembleDebug

Requires Java 7+ and Android SDK tools compatible with compileSdkVersion 19.

Daily commands:

./gradlew :sample:installDebug
# Then run the app on an emulator or device via Android Studio, or:
adb shell am start -n com.etsy.android.sample/.MainActivity

The sample app has three activities: MainActivity (menu), StaggeredGridActivity (main demo), and ListViewActivity (comparison view).

🗺️Map of the codebase

  • library/src/main/java/com/etsy/android/grid/StaggeredGridView.java — Core widget implementation—contains all staggered layout logic, column management, and view rendering; must understand this to modify grid behavior
  • library/src/main/java/com/etsy/android/grid/ExtendableListView.java — Base ListView extension that StaggeredGridView relies on; handles scrolling, recycling, and list adaptation
  • library/src/main/java/com/etsy/android/grid/HeaderViewListAdapter.java — Adapter wrapper for header/footer support; must review before adding custom header logic
  • library/src/main/java/com/etsy/android/grid/util/DynamicHeightImageView.java — Utility for maintaining aspect ratios in staggered layouts; critical for correct column height calculations
  • library/src/main/res/values/attrs.xml — XML attribute definitions for StaggeredGridView configuration; defines all customizable parameters
  • sample/src/main/java/com/etsy/android/sample/StaggeredGridActivity.java — Primary example activity showing grid setup and usage patterns; reference for integration

🛠️How to make changes

Configure Column Count for Screen Orientation

  1. Define columnCount (portrait) and columnCountLandscape in XML layout or via attrs (sample/src/main/res/layout/activity_sgv.xml)
  2. Set columns programmatically in activity using setColumnCount() or setColumnCountPortrait()/setColumnCountLandscape() (sample/src/main/java/com/etsy/android/sample/StaggeredGridActivity.java)
  3. Review StaggeredGridView attribute definitions to understand available configuration options (library/src/main/res/values/attrs.xml)

Create a Custom Grid Item View

  1. Extend DynamicHeightImageView or DynamicHeightTextView to maintain aspect ratio consistency (library/src/main/java/com/etsy/android/grid/util/DynamicHeightImageView.java)
  2. Create a custom adapter extending BaseAdapter and override getView() to use your custom view (sample/src/main/java/com/etsy/android/sample/SampleAdapter.java)
  3. Bind your adapter to StaggeredGridView in the activity using setAdapter() (sample/src/main/java/com/etsy/android/sample/StaggeredGridActivity.java)

Add Headers and Footers to Grid

  1. Review HeaderViewListAdapter which wraps adapters to support header/footer views (library/src/main/java/com/etsy/android/grid/HeaderViewListAdapter.java)
  2. Call addHeaderView() and addFooterView() on StaggeredGridView with your custom view layouts (sample/src/main/java/com/etsy/android/sample/StaggeredGridEmptyViewActivity.java)
  3. Check sample layout for header/footer structure (sample/src/main/res/layout/list_item_header_footer.xml)

Handle Grid Scrolling and View Recycling

  1. Understand ExtendableListView's scroll behavior and view recycling mechanism (library/src/main/java/com/etsy/android/grid/ExtendableListView.java)
  2. Implement view recycling in your adapter's getView() method using convertView parameter (sample/src/main/java/com/etsy/android/sample/SampleAdapter.java)
  3. Monitor scroll callbacks via setOnScrollListener() in activity for infinite scroll patterns (sample/src/main/java/com/etsy/android/sample/StaggeredGridActivity.java)

🔧Why these technologies

  • ExtendableListView (ListView extension) — Leverages proven Android ListView recycling and scrolling mechanisms rather than building from scratch; reduces memory footprint and improves performance for large datasets
  • Custom measurement & layout calculations — ListView alone doesn't support multi-column staggered layouts; custom logic distributes items across columns dynamically based on aspect ratios
  • DynamicHeightImageView/TextView utilities — Maintains consistent column widths while accommodating varying item heights; aspect ratio scaling prevents distortion in staggered layouts
  • HeaderViewListAdapter wrapper — Adds header/footer support without modifying core StaggeredGridView; uses adapter pattern for composition and separation of concerns

⚖️Trade-offs already made

  • Built on ListView instead of custom ViewGroup

    • Why: ListView provides free recycling, scrolling, and touch handling—significantly reduces implementation complexity
    • Consequence: Inherits ListView limitations: less fine-grained control over layout, bound by ListAdapter interface, not as flexible as modern RecyclerView
  • Calculates column heights dynamically per item rather than predictively

    • Why: Supports variable item heights (images with different aspect ratios, dynamic text); no need for item height pre-declaration
    • Consequence: Layout is non-deterministic until rendered; occasional visual janking during rapid scrolls if measurement is slow
  • Stores column height state in memory

    • Why: Fast O(1) lookup for next column to fill; enables smooth scrolling without recalculation
    • Consequence: Memory overhead grows with number of items; state can become stale if adapter data changes
  • Single-threaded layout on main thread

    • Why: Android UI framework requires all layout on main thread; simplifies synchronization
    • Consequence: Heavy item measurement (e.g., complex image loading) can block UI; not suitable for 1000+ item grids without pagination

🚫Non-goals (don't propose these)

  • Real-time, animated insertion/deletion of grid items
  • Infinite scroll with automatic network pagination
  • Support for variable row heights (only column-based staggering)
  • Android API compatibility below level 4
  • Accessibility features (TalkBack, screen reader optimization)
  • RTL (right-to-left) language layout

🪤Traps & gotchas

  1. This library is officially deprecated and abandoned—do not use in new projects; Etsy itself moved to RecyclerView internally. 2. Very old SDK target (compileSdkVersion 19, ~2014 era)—will cause build failures or warnings on modern Android Studio / build tools without manual gradle tweaking. 3. No tests visible in the file structure; the library relies entirely on manual testing via the sample app. 4. AbsListView extends: The widget mimics AbsListView but doesn't fully implement all its contracts, so swapping it into code expecting true ListView compatibility may cause edge-case crashes. 5. View recycling is manual: Unlike RecyclerView's automatic recycling, you must size items correctly or face off-by-one layout bugs.

🏗️Architecture

💡Concepts to learn

  • Staggered/Masonry Layout — Understanding how StaggeredGridView distributes items into variable-height rows across multiple columns is central to using this widget correctly and predicting layout behavior.
  • View Recycling & AdapterView Pattern — This widget implements manual view recycling (like ListView) to reuse views as users scroll, avoiding memory bloat; mastering this pattern is essential for Android performance.
  • Parcelable State Persistence — The ClassLoaderSavedState.java class persists grid scroll position across orientation changes using Parcelable; critical for smooth UX in portrait/landscape switches.
  • Custom ViewGroup Measurement & Layout — StaggeredGridView's core algorithm measures child views with varying heights and lays them into columns; understanding Android's measure/layout cycle is prerequisite for modifying this logic.
  • AbsListView Extension & Compatibility — This widget partially extends AbsListView to leverage familiar ListView contracts (OnScrollListener, adapters); knowing its limitations versus true ListView prevents subtle bugs.
  • XML Custom Attributes (attrs.xml) — The library exposes configuration (columnCount, itemMargin, etc.) via custom XML attributes in attrs.xml; understanding how to define and parse these is essential for customizing the widget.
  • Aspect Ratio Preservation in Dynamic ViewsDynamicHeightImageView scales children to fit column width while preserving aspect ratio; this pattern is fundamental for photo grids and content-driven layouts.
  • android/architecture-samples — Google's official guide to modern Android architecture patterns; teaches RecyclerView + ViewModel + LiveData as the replacement for ListView/StaggeredGridView.
  • google/flexbox-layout — Google's flexible box layout library for Android; another post-ListView grid solution offering different layout flexibility than staggered grids.
  • pinterest/pinterest-sdk-android — Pinterest's own Android SDK, historically the inspiration for masonry/staggered layouts; shows how the feature's originators approach grid UI in modern Android.
  • square/picasso — Image loading library that integrates seamlessly with views like StaggeredGridView for efficient image display in grids; commonly paired with this library in real apps.
  • etsy/AndroidStaggeredGrid-fork-by-community — Community maintains modern forks (e.g., on JCenter) with updated SDK targets and RecyclerView wrappers; check if a maintained fork exists for legacy support.

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add unit tests for StaggeredGridView core layout logic

The repo lacks any test directory structure. StaggeredGridView.java contains complex layout calculations and column management that are critical to functionality but completely untested. This is especially important given the library's deprecation status - tests would help prevent regressions and make maintenance easier for users who fork it.

  • [ ] Create library/src/test/java/com/etsy/android/grid/ directory structure
  • [ ] Add unit tests for StaggeredGridView.java covering column width calculations, item positioning, and varying row heights
  • [ ] Add tests for ExtendableListView.java layout behavior
  • [ ] Configure build.gradle with JUnit and Mockito dependencies for testing
  • [ ] Add README section documenting how to run tests

Migrate sample app to use RecyclerView with StaggeredGridLayoutManager

The README explicitly recommends RecyclerView over this deprecated library. Creating a migration guide with a parallel sample implementation (StaggeredGridActivity vs a new RecyclerViewStaggeredActivity) would be invaluable for users trying to understand the modern approach and migrate their code.

  • [ ] Create sample/src/main/java/com/etsy/android/sample/RecyclerViewStaggeredActivity.java as a modern alternative implementation
  • [ ] Add layout file sample/src/main/res/layout/activity_recyclerview_staggered.xml
  • [ ] Create MIGRATION.md documenting API differences and code examples for converting from StaggeredGridView to RecyclerView + StaggeredGridLayoutManager
  • [ ] Update sample/src/main/res/menu to include option to launch both old and new implementations for side-by-side comparison

Add Espresso/instrumentation tests for sample app UI interactions

The sample app (MainActivity, StaggeredGridActivity, StaggeredGridEmptyViewActivity) has no automated UI tests. Adding instrumentation tests would verify the grid renders correctly with varying column counts, handles empty states properly, and scrolling/layout works across different screen sizes.

  • [ ] Create sample/src/androidTest/java/com/etsy/android/sample/ directory structure
  • [ ] Add Espresso instrumentation tests for StaggeredGridActivity.java covering grid rendering with different column layouts
  • [ ] Add tests for StaggeredGridEmptyViewActivity.java empty state behavior
  • [ ] Add tests verifying responsive behavior between portrait (values/integers.xml) and landscape (values-land/integers.xml) configurations
  • [ ] Update build.gradle with Espresso dependencies and instrumentation test runner configuration

🌿Good first issues

  • Add unit tests for StaggeredGridView column calculation logic in library/src/test/ (currently no test directory exists). Start with tests for setColumnCount() and how it distributes items across columns.
  • Document the full XML attribute set in a dedicated ATTRIBUTES.md file with examples. Currently only columnCount, itemMargin, and others are defined in attrs.xml but lack usage documentation.
  • Add a third sample activity (e.g., StaggeredGridAnimationActivity.java) demonstrating item add/remove animations, since the current samples only show static grids.

Top contributors

Click to expand
  • @denizmveli — 51 commits
  • [@Deniz Veli](https://github.com/Deniz Veli) — 12 commits
  • @xrigau — 5 commits
  • @DougSimonton — 4 commits
  • [@Jens Driller](https://github.com/Jens Driller) — 4 commits

📝Recent commits

Click to expand
  • 97739f6 — Added deprecated notice to README (Deniz Veli)
  • d8ca7e6 — Merge pull request #98 from DougSimonton/master (denizmveli)
  • fa002aa — Simplified isLandscape() call (denizmveli)
  • 28a2e48 — Build updates to support Android Studio 0.8.1, Gradle 1.12, Plugin 0.12, BuildTools 20.0.0 & custom maven release fork t (denizmveli)
  • ba5070b — Merge pull request #88 from jstralko/master (denizmveli)
  • 733bfb5 — Removed conflicting fix (DougSimonton)
  • 73f6e1b — Properly detect the orientation of the device. (jstralko)
  • ae56179 — Added long press handler to sample (DougSimonton)
  • 12a713d — Added checks for motionPosition >= 0 (DougSimonton)
  • 8fe5f82 — Readme cleanup (denizmveli)

🔒Security observations

This Android library has critical security issues stemming from severely outdated dependencies and build configurations. The project uses Gradle 0.12.+ and targets Android API 19 (from 2013), both of which lack modern security patches and features. Additionally, the library has been officially deprecated since 2015 and receives no security updates. The codebase lacks visible security configuration and dependency verification mechanisms. Users should migrate to modern alternatives like RecyclerView with StaggeredGridLayoutManager. The extremely low security score reflects the accumulation of unpatched legacy components that pose significant risk in current Android ecosystems.

  • High · Outdated Gradle and Build Tools — build.gradle, gradle.properties. The project uses Gradle 0.12.+ (from 2014) and Android Build Tools 20.0.0, which are severely outdated and contain known security vulnerabilities. These old versions lack security patches and may have unpatched CVEs. Fix: Update to the latest stable versions of Gradle (8.x+) and Android Build Tools (34.x+). Ensure all dependencies use current versions with security patches.
  • High · Outdated Android Compilation Target — build.gradle. The project targets compileSdkVersion 19 (Android 4.4, released 2013), which is extremely outdated. This misses critical security features and patches introduced in modern Android versions, leaving the library vulnerable to known exploits. Fix: Update compileSdkVersion to at least 34 (Android 14). Ensure targetSdkVersion is also updated to meet current Google Play Store requirements.
  • Medium · Insecure Repository Configuration — build.gradle. The build configuration only uses mavenCentral() without any repository pinning or checksum verification. While mavenCentral is reputable, there's no defense against repository compromise or man-in-the-middle attacks. Fix: Implement dependency verification using Gradle's built-in dependency verification feature. Consider adding checksum validation for critical dependencies.
  • Medium · No Explicit Security Configuration — library/src/main/AndroidManifest.xml, sample/src/main/AndroidManifest.xml. The AndroidManifest.xml files are not visible in the provided structure, but the absence of security-related configurations (like android:usesCleartextTraffic=false) cannot be verified. This could allow unencrypted network traffic. Fix: Verify that android:usesCleartextTraffic is explicitly set to 'false' in AndroidManifest.xml. Implement network security configuration file to enforce HTTPS/TLS.
  • Low · Deprecated Library Status — README.md. The library is officially deprecated (as of 09-2015) and no longer receives updates or security patches. Using deprecated software increases long-term security risk. Fix: Migrate to Google's RecyclerView with StaggeredGridLayoutManager as recommended by Etsy. This is an active, maintained solution with regular security updates.
  • Low · Lack of Proguard/R8 Configuration Visibility — library/proguard-rules.pro (missing). No proguard-rules.pro or R8 configuration file is visible in the file structure. This means the library may not be properly obfuscated, which could expose sensitive logic or make reverse engineering easier. Fix: Create and configure a proguard-rules.pro file with appropriate obfuscation rules. Enable R8 minification in release builds to protect code.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Concerning signals · etsy/AndroidStaggeredGrid — RepoPilot