需要源码和图片集请点赞关注收藏后评论区留言或者私信~~~
各家电商的App首页都是动感十足,页面元素丰富令人眼花缭乱,其中运用了Android的多种组合控件,可以说是App界面开发的集大成之作,下面我们也动手实现一个。
本次项目主要模仿淘宝App采用的技术,所以有底部标签栏,顶部也有标题栏,并且对于分类页面的商品列表,也会有高低不一呈现的瀑布流效果
界面主要用到了以下控件
1:底部标签栏
2:广告条
3:循环视图RecyclerView
4:工具栏Toolbar
5:标签布局TabLaout
6:第二代翻页视图
7:循环视图的瀑布流布局
8:下拉刷新布局
1:在ScrollView内部添加RecyclerView
2:关于ViewPager+Fragment的多重嵌套
3:电商首页项目的源码之间关系
与本次项目主要有关的代码之间关系如下
1:DepartmentStoreActivity.java 这是电商App首页的入口代码
2:DepartmentPagerAdapter.java 这是电商首页集成3个碎片页的翻页适配代码
3:DepartmentHomeFragment.java 这是首页标签对应的碎片代码
4:DepartmentClassFragment.java 这是分类标签对应的碎片代码
5:DepartmentCartFragment.java 这是购物车标签对应的碎片代码
首页效果如下

点击下方的标签栏可切换

点击上方的标签栏可以切换到时装频道

购物车频道可以参见我之前的博客

package com.example.chapter12;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager.widget.ViewPager;
import android.os.Bundle;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import com.example.chapter12.adapter.DepartmentPagerAdapter;
public class DepartmentStoreActivity extends AppCompatActivity {
private ViewPager vp_content; // 声明一个翻页视图对象
private RadioGroup rg_tabbar; // 声明一个单选组对象
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_department_store);
vp_content = findViewById(R.id.vp_content);
// 构建一个翻页适配器
DepartmentPagerAdapter adapter = new DepartmentPagerAdapter(getSupportFragmentManager());
vp_content.setAdapter(adapter); // 设置翻页视图的适配器
// 给翻页视图添加页面变更监听器
vp_content.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
// 选中指定位置的单选按钮
rg_tabbar.check(rg_tabbar.getChildAt(position).getId());
}
});
rg_tabbar = findViewById(R.id.rg_tabbar);
// 设置单选组的选中监听器
rg_tabbar.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
for (int pos=0; pos<rg_tabbar.getChildCount(); pos++) {
// 获得指定位置的单选按钮
RadioButton tab = (RadioButton) rg_tabbar.getChildAt(pos);
if (tab.getId() == checkedId) { // 正是当前选中的按钮
vp_content.setCurrentItem(pos); // 设置翻页视图显示第几页
}
}
}
});
}
}
package com.example.chapter12.adapter;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import com.example.chapter12.fragment.DepartmentHomeFragment;
import com.example.chapter12.fragment.DepartmentClassFragment;
import com.example.chapter12.fragment.DepartmentCartFragment;
public class DepartmentPagerAdapter extends FragmentPagerAdapter {
// 碎片页适配器的构造方法,传入碎片管理器
public DepartmentPagerAdapter(FragmentManager fm) {
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
}
// 获取指定位置的碎片Fragment
public Fragment getItem(int position) {
if (position == 0) {
return new DepartmentHomeFragment();
} else if (position == 1) {
return new DepartmentClassFragment();
} else if (position == 2) {
return new DepartmentCartFragment();
} else {
return null;
}
}
// 获取碎片Fragment的个数
public int getCount() {
return 3;
}
}
package com.example.chapter12.fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import com.example.chapter12.R;
public class DepartmentCartFragment extends Fragment {
protected View mView; // 声明一个视图对象
protected AppCompatActivity mActivity; // 声明一个活动对象
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mActivity = (AppCompatActivity) getActivity();
mView = inflater.inflate(R.layout.fragment_department_cart, container, false);
// 从布局文件中获取名叫tl_head的工具栏
Toolbar tl_head = mView.findViewById(R.id.tl_head);
tl_head.setTitle("购物车"); // 设置工具栏的标题文字
mActivity.setSupportActionBar(tl_head); // 使用tl_head替换系统自带的ActionBar
return mView;
}
}
2
package com.example.chapter12.fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.viewpager2.widget.ViewPager2;
import com.example.chapter12.R;
import com.example.chapter12.adapter.ClassPagerAdapter;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
import java.util.ArrayList;
import java.util.List;
public class DepartmentClassFragment extends Fragment {
protected View mView; // 声明一个视图对象
protected AppCompatActivity mActivity; // 声明一个活动对象
private List<String> mTitleList = new ArrayList<String>(); // 标题文字列表
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mActivity = (AppCompatActivity) getActivity();
mView = inflater.inflate(R.layout.fragment_department_class, container, false);
// 从布局文件中获取名叫tl_head的工具栏
Toolbar tl_head = mView.findViewById(R.id.tl_head);
mActivity.setSupportActionBar(tl_head); // 使用tl_head替换系统自带的ActionBar
mTitleList.add("服装");
mTitleList.add("电器");
// 从布局文件中获取名叫tab_title的标签布局
TabLayout tab_title = mView.findViewById(R.id.tab_title);
// 从布局文件中获取名叫vp2_content的二代翻页视图
ViewPager2 vp2_content = mView.findViewById(R.id.vp2_content);
// 构建一个分类信息的翻页适配器。注意Fragment嵌套时要传getChildFragmentManager
ClassPagerAdapter adapter = new ClassPagerAdapter(mActivity, mTitleList);
vp2_content.setAdapter(adapter); // 设置二代翻页视图的适配器
// 把标签布局跟翻页视图通过指定策略连为一体,二者在页面切换时一起联动
new TabLayoutMediator(tab_title, vp2_content, new TabLayoutMediator.TabConfigurationStrategy() {
@Override
public void onConfigureTab(TabLayout.Tab tab, int position) {
tab.setText(mTitleList.get(position)); // 设置每页的标签文字
}
}).attach();
return mView;
}
}
3
package com.example.chapter12.fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.example.chapter12.R;
import com.example.chapter12.adapter.MobileGridAdapter;
import com.example.chapter12.adapter.MobileRecyclerAdapter;
import com.example.chapter12.adapter.RecyclerCombineAdapter;
import com.example.chapter12.adapter.RecyclerGridAdapter;
import com.example.chapter12.bean.GoodsInfo;
import com.example.chapter12.bean.NewsInfo;
import com.example.chapter12.util.Utils;
import com.example.chapter12.widget.BannerPager;
import com.example.chapter12.widget.SpacesDecoration;
import java.util.ArrayList;
import java.util.List;
public class DepartmentHomeFragment extends Fragment implements BannerPager.BannerClickListener {
protected View mView; // 声明一个视图对象
protected AppCompatActivity mActivity; // 声明一个活动对象
private List<Integer> getImageList() {
ArrayList<Integer> imageList = new ArrayList<Integer>();
imageList.add(R.drawable.banner_1);
imageList.add(R.drawable.banner_2);
imageList.add(R.drawable.banner_3);
imageList.add(R.drawable.banner_4);
imageList.add(R.drawable.banner_5);
return imageList;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mActivity = (AppCompatActivity) getActivity();
mView = inflater.inflate(R.layout.fragment_department_home, container, false);
// 从布局文件中获取名叫tl_head的工具栏
Toolbar tl_head = mView.findViewById(R.id.tl_head);
tl_head.setTitle("商城首页"); // 设置工具栏的标题文字
mActivity.setSupportActionBar(tl_head); // 使用tl_head替换系统自带的ActionBar
initBanner(); // 初始化广告轮播条
initGrid(); // 初始化市场网格列表
initCombine(); // 初始化猜你喜欢的商品展示网格
initPhone(); // 初始化手机网格列表
return mView;
}
private void initBanner() {
// 从布局文件中获取名叫banner_pager的广告轮播条
BannerPager banner = mView.findViewById(R.id.banner_pager);
// 获取广告轮播条的布局参数
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) banner.getLayoutParams();
params.height = (int) (Utils.getScreenWidth(mActivity) * 250f / 640f);
banner.setLayoutParams(params); // 设置广告轮播条的布局参数
banner.setImage(getImageList()); // 设置广告轮播条的广告图片列表
banner.setOnBannerListener(this); // 设置广告轮播条的广告点击监听器
banner.start(); // 开始轮播广告图片
}
// 一旦点击了广告图,就回调监听器的onBannerClick方法
public void onBannerClick(int position) {
String desc = String.format("您点击了第%d张图片", position + 1);
Toast.makeText(mActivity, desc, Toast.LENGTH_LONG).show();
}
private void initGrid() {
// 从布局文件中获取名叫rv_grid的循环视图
RecyclerView rv_grid = mView.findViewById(R.id.rv_grid);
// 创建一个网格布局管理器
GridLayoutManager manager = new GridLayoutManager(mActivity, 5);
rv_grid.setLayoutManager(manager); // 设置循环视图的布局管理器
// 构建一个市场列表的网格适配器
RecyclerGridAdapter adapter = new RecyclerGridAdapter(mActivity, NewsInfo.getDefaultGrid());
adapter.setOnItemClickListener(adapter); // 设置网格列表的点击监听器
adapter.setOnItemLongClickListener(adapter); // 设置网格列表的长按监听器
rv_grid.setAdapter(adapter); // 设置循环视图的网格适配器
rv_grid.setItemAnimator(new DefaultItemAnimator()); // 设置循环视图的动画效果
rv_grid.addItemDecoration(new SpacesDecoration(1)); // 设置循环视图的空白装饰
}
private void initCombine() {
// 从布局文件中获取名叫rv_combine的循环视图
RecyclerView rv_combine = mView.findViewById(R.id.rv_combine);
// 创建一个四列的网格布局管理器
GridLayoutManager manager = new GridLayoutManager(mActivity, 4);
// 设置网格布局管理器的占位规则
// 以下占位规则的意思是:第一项和第二项占两列,其它项占一列;
// 如果网格的列数为四,那么第一项和第二项平分第一行,第二行开始每行有四项。
manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
if (position == 0 || position == 1) { // 为第一项或者第二项
return 2; // 占据两列
} else { // 为其它项
return 1; // 占据一列
}
}
});
rv_combine.setLayoutManager(manager); // 设置循环视图的布局管理器
// 构建一个猜你喜欢的网格适配器
RecyclerCombineAdapter adapter = new RecyclerCombineAdapter(mActivity, NewsInfo.getDefaultCombine());
adapter.setOnItemClickListener(adapter); // 设置网格列表的点击监听器
adapter.setOnItemLongClickListener(adapter); // 设置网格列表的长按监听器
rv_combine.setAdapter(adapter); // 设置循环视图的网格适配器
rv_combine.setItemAnimator(new DefaultItemAnimator()); // 设置循环视图的动画效果
rv_combine.addItemDecoration(new SpacesDecoration(1)); // 设置循环视图的空白装饰
}
private void initPhone() {
// 从布局文件中获取名叫rv_phone的循环视图
RecyclerView rv_phone = mView.findViewById(R.id.rv_phone);
// 创建一个网格布局管理器
GridLayoutManager manager = new GridLayoutManager(mActivity, 3);
rv_phone.setLayoutManager(manager); // 设置循环视图的布局管理器
// 构建一个手机列表的循环适配器
MobileGridAdapter adapter = new MobileGridAdapter(mActivity, GoodsInfo.getDefaultList());
rv_phone.setAdapter(adapter); // 设置循环视图的网格适配器
adapter.setOnItemClickListener(adapter); // 设置网格列表的点击监听器
adapter.setOnItemLongClickListener(adapter); // 设置网格列表的长按监听器
rv_phone.setItemAnimator(new DefaultItemAnimator()); // 设置循环视图的动画效果
rv_phone.addItemDecoration(new SpacesDecoration(1)); // 设置循环视图的空白装饰
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.viewpager.widget.ViewPager
android:id="@+id/vp_content"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<RadioGroup
android:id="@+id/rg_tabbar"
android:layout_width="match_parent"
android:layout_height="60dp"
android:orientation="horizontal">
<RadioButton
android:id="@+id/rb_home"
style="@style/TabButton"
android:checked="true"
android:text="首页"
android:drawableTop="@drawable/tab_first_selector" />
<RadioButton
android:id="@+id/rb_class"
style="@style/TabButton"
android:text="分类"
android:drawableTop="@drawable/tab_second_selector" />
<RadioButton
android:id="@+id/rb_cart"
style="@style/TabButton"
android:text="购物车"
android:drawableTop="@drawable/tab_third_selector" />
</RadioGroup>
</LinearLayout>
创作不易 觉得有帮助请点赞关注收藏~~~
如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby
我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当
我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘
我已经像这样安装了一个新的Rails项目:$railsnewsite它执行并到达:bundleinstall但是当它似乎尝试安装依赖项时我得到了这个错误Gem::Ext::BuildError:ERROR:Failedtobuildgemnativeextension./System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/rubyextconf.rbcheckingforlibkern/OSAtomic.h...yescreatingMakefilemake"DESTDIR="cleanmake"DESTDIR="
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden
我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm
我在app/helpers/sessions_helper.rb中有一个帮助程序文件,其中包含一个方法my_preference,它返回当前登录用户的首选项。我想在集成测试中访问该方法。例如,这样我就可以在测试中使用getuser_path(my_preference)。在其他帖子中,我读到这可以通过在测试文件中包含requiresessions_helper来实现,但我仍然收到错误NameError:undefinedlocalvariableormethod'my_preference'.我做错了什么?require'test_helper'require'sessions_hel
我是Google云的新手,我正在尝试对其进行首次部署。我的第一个部署是RubyonRails项目。我基本上是在关注thisguideinthegoogleclouddocumentation.唯一的区别是我使用的是我自己的项目,而不是他们提供的“helloworld”项目。这是我的app.yaml文件runtime:customvm:trueentrypoint:bundleexecrackup-p8080-Eproductionconfig.ruresources:cpu:0.5memory_gb:1.3disk_size_gb:10当我转到我的项目目录并运行gcloudprevie
我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI
当我在我的Rails应用程序根目录中运行rakedoc:app时,API文档是使用/doc/README_FOR_APP作为主页生成的。我想向该文件添加.rdoc扩展名,以便它在GitHub上正确呈现。更好的是,我想将它移动到应用程序根目录(/README.rdoc)。有没有办法通过修改包含的rake/rdoctask任务在我的Rakefile中执行此操作?是否有某个地方可以查找可以修改的主页文件的名称?还是我必须编写一个新的Rake任务?额外的问题:Rails应用程序的两个单独文件/README和/doc/README_FOR_APP背后的逻辑是什么?为什么不只有一个?