Tab经常和Fragment结合使用,这一讲我们用3种方式来实现这种快捷导航。

0、重要的两个监听器

MyTabListener,这个我们之前已经接触过了

package com.kale.actionbar05;

import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBar.Tab; /**
* @author:Jack Tony
* @tips :设置tab的监听器,控制viewpager的显示
* @date :2014-7-30
*/
public class MyTabListener implements ActionBar.TabListener{
ViewPager viewPager;
public MyTabListener(ViewPager viewPager) {
this.viewPager =viewPager;
} @Override
public void onTabReselected(Tab arg0, FragmentTransaction arg1) { } @Override
public void onTabSelected(Tab tab, FragmentTransaction arg1) {
if (viewPager != null && viewPager.getCurrentItem() != tab.getPosition()){
viewPager.setCurrentItem(tab.getPosition());
}
} @Override
public void onTabUnselected(Tab arg0, FragmentTransaction arg1) { } }

PageChangeListener(这里面是滑动ViewPager会触发的动作)

package com.kale.actionbar05;

import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar; public class PageChangeListener implements ViewPager.OnPageChangeListener { private ActionBar actionBar; public PageChangeListener(ActionBar actionBar) {
this.actionBar = actionBar;
} @Override
public void onPageScrollStateChanged(int arg0) {
// TODO 自动生成的方法存根 } @Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO 自动生成的方法存根 } @Override
public void onPageSelected(int position) {
//System.out.println("position:" + position);
actionBar.setSelectedNavigationItem(position); }
}

一、Tab+ViewPager

先定义一个主布局,里面放入ViewPager。我们即将在这个ViewPager中放入视图

view_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"> <android.support.v4.view.ViewPager
android:id="@+id/viewPager01"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible" /> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:text="下面是ViewPager"
android:textAppearance="?android:attr/textAppearanceLarge" /> </RelativeLayout>

view_item.xml

 这是准备放入ViewPager的视图。我们放3个TextView,然后再放入这整个视图

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" > <ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center_horizontal"
android:src="@drawable/ic_launcher" /> <TextView
android:id="@+id/viewItem_textView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="No 4"
android:textSize="40sp"/> </LinearLayout>

ViewMainActivity

package com.kale.actionbar05.view;

import java.util.ArrayList;

import com.kale.actionbar05.MyTabListener;
import com.kale.actionbar05.PageChangeListener;
import com.kale.actionbar05.R; import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView; public class ViewMainActivity extends ActionBarActivity {
private ActionBar actionBar;
private ArrayList<View> viewList;
ViewPager viewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.view_main);
initView();
initActionBar(); viewList = new ArrayList<View>();
//得到填充器,准备获得对象
LayoutInflater mLayoutInflater = getLayoutInflater();
//给适配器准备好数据,添加进list中
for (int i = 0; i < 3; i++) {
TextView tv = (TextView)(mLayoutInflater.inflate(R.layout.view_item, null))
.findViewById(R.id.viewItem_textView);
tv.setText("No "+(i+1));
viewList.add(tv);
}
viewList.add(mLayoutInflater.inflate(R.layout.view_item, null));//也可以直接用布局做一个视图
//内容准备好后,设置适配器
viewPager.setAdapter(new MyPagerAdapter(viewList));
viewPager.setOnPageChangeListener(new PageChangeListener(getSupportActionBar())); } /**
*初始化actionBar和Tab
*/
private void initActionBar() {
actionBar = getSupportActionBar();
//设定有Tab
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
ActionBar.Tab tab;
//开始添加Tab
for (int i = 1; i <= 4; i++) {
tab = actionBar.newTab();
tab.setText("Tab " + i);
tab.setTabListener(new MyTabListener(viewPager));
actionBar.addTab(tab);
}
} private void initView() {
viewPager = (ViewPager)findViewById(R.id.viewPager01);
} }

MyPagerAdapter(这里设置适配器,从list中得到一个个View,然后展现出来)

package com.kale.actionbar05.view;

import java.util.ArrayList;

import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewGroup; public class MyPagerAdapter extends PagerAdapter {
private ArrayList<View> mViewList;
private int pagerNum = 0; public MyPagerAdapter(ArrayList<View> viewList) {
mViewList = viewList;
} public int getPagerNum() {
return pagerNum;
} @Override
public int getCount() {
return mViewList.size();
} @Override
public boolean isViewFromObject(View arg0, Object arg1) {
return arg0 == arg1;
} @Override
public void destroyItem(View arg0, int arg1, Object arg2) {
if (mViewList.get(arg1) != null) {
((ViewPager) arg0).removeView(mViewList.get(arg1));
}
} @Override
public Object instantiateItem(View arg0, int arg1) {
try {
if (mViewList.get(arg1).getParent() == null) {
((ViewPager) arg0).addView(mViewList.get(arg1), 0);
} else {
/*
* 很难理解新添加进来的view会自动绑定一个父类,由于一个儿子view不能与两个父类相关, 所以得解绑不这样做否则会产生
* viewpager java.lang.IllegalStateException: The specified
* child already has a parent. You must call removeView() on the
* child's parent first.
*/
((ViewGroup) mViewList.get(arg1).getParent())
.removeView(mViewList.get(arg1));
((ViewPager) arg0).addView(mViewList.get(arg1), 0);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
pagerNum = arg1;
}
return mViewList.get(arg1);
} }

二、Tab+Fragment+ViewPager

fragment_main.xml (主布局)

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"> <android.support.v4.view.ViewPager
android:id="@+id/viewPager02"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible" /> </RelativeLayout>

DummyFragment(准备填充进去的Fragment,这里面接受一个参数用来方便说明)

package com.kale.actionbar05.fragment;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView; public class DummyFragment extends Fragment { public static final String BUNDLE_SECTION_NUMBER = "section_number"; // 该返回值就是这个Fragment显示的view组件
@Override
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
TextView textView = new TextView(getActivity());
textView.setGravity(Gravity.CENTER);
// 获取创建该Fragment时传入的参数的Bundle
Bundle bundle = getArguments();
textView.setText("Fragment 0" + bundle.getInt(BUNDLE_SECTION_NUMBER));
textView.setTextSize(50);
return textView;
}
}

FragmentMainActivity

package com.kale.actionbar05.fragment;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.view.ViewGroup; import com.kale.actionbar05.MyTabListener;
import com.kale.actionbar05.PageChangeListener;
import com.kale.actionbar05.R; /**
* @author:Jack Tony
* @tips :出于使用FragmentPagerAdapter 时,Fragment对象会一直存留在内存中.
* 所以当有大量的显示页时,就不适合用FragmentPagerAdapter了
* ,FragmentPagerAdapter适用于只有少数的page情况。
* 多的话请考虑使用FragmentStatePagerAdapter,当使用FragmentStatePagerAdapter时,
* 如果Fragment不显示,那么Fragment对象会被销毁,但在回调onDestroy()方法之前
* 会回调onSaveInstanceState(Bundle outState)方法来保存Fragment的状态,
* 下次Fragment显示时通过onCreate(Bundle savedInstanceState)把存储的状态值取出来,
* FragmentStatePagerAdapter 比较适合页面比较多的情况,像一个页面的ListView
* @date :2014-7-30
*/
public class FragmentMainActivity extends ActionBarActivity {
ActionBar actionBar;
ViewPager viewPager; @Override
protected void onCreate(Bundle savedInstanceState) {
// TODO 自动生成的方法存根
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_main);
initView();
initActionBar();
viewPager.setAdapter(new MyFragmentPagerAdapter(
getSupportFragmentManager()));
//下面是用另一种适配器的方式,其实就是换个名字。主要针对的是很多个fragment的情况
//viewPager.setAdapter(new MyFragmentStaticAdapter(getSupportFragmentManager()));
viewPager.setOnPageChangeListener(new PageChangeListener(actionBar)); } public class MyFragmentPagerAdapter extends FragmentPagerAdapter {
private int NUM_ITEMS = 4; public MyFragmentPagerAdapter(FragmentManager fm) {
super(fm);
} @Override
public Fragment getItem(int position) {
Fragment fragment = new DummyFragment();
Bundle args = new Bundle();
args.putInt(DummyFragment.BUNDLE_SECTION_NUMBER, position + 1);
fragment.setArguments(args);
return fragment;
} @Override
public int getCount() {
return NUM_ITEMS;
} @Override
public void destroyItem(ViewGroup container, int position, Object object) {
// 这里Destroy的是Fragment的视图层次,并不是Destroy Fragment对象
super.destroyItem(container, position, object);
//Log.i("INFO", "Destroy Item " + position);
} } private void initView() {
viewPager = (ViewPager) findViewById(R.id.viewPager02);
} /**
* 初始化actionBar和Tab
*/
private void initActionBar() {
actionBar = getSupportActionBar();
// 设定有Tab
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
ActionBar.Tab tab;
// 开始添加Tab
for (int i = 1; i <= 4; i++) {
tab = actionBar.newTab();
tab.setText("Tab " + i);
tab.setTabListener(new MyTabListener(viewPager));
actionBar.addTab(tab);
}
} public class MyFragmentStaticAdapter extends FragmentStatePagerAdapter {
private int NUM_ITEMS = 5; public MyFragmentStaticAdapter(FragmentManager fm) {
super(fm);
} @Override
public Fragment getItem(int position) {
Fragment fragment = new DummyFragment();
Bundle args = new Bundle();
args.putInt(DummyFragment.BUNDLE_SECTION_NUMBER, position + 1);
fragment.setArguments(args);
return fragment;
} @Override
public int getCount() {
return NUM_ITEMS;
} }
}

三、PagerSlidingTabStrip+ViewPager+Fragment

tab_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" xmlns:app="http://schemas.android.com/apk/res/com.kale.actionbar05"> <com.astuetz.PagerSlidingTabStrip
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="40dp"
app:pstsShouldExpand="true"
app:pstsUnderlineHeight="1dp"
app:pstsIndicatorHeight="4dp"
app:pstsIndicatorColor="#45c01a"
app:pstsSelectedTabTextColor="#45c01a"
app:pstsTabTextSize="16sp"
app:pstsTabTextColor="#666666"
app:pstsDividerColor="@android:color/transparent"
/> <android.support.v4.view.ViewPager
android:id="@+id/viewPager03"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible" /> </LinearLayout>
ChatFragment (其中的一个Fragment,其余的都一样,就是文字变了)
package com.kale.actionbar05.tab;

import android.app.ActionBar.LayoutParams;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextView; public class ChatFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
FrameLayout fl = new FrameLayout(getActivity());
fl.setLayoutParams(params);
DisplayMetrics dm = getResources().getDisplayMetrics();
final int margin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, dm);
TextView v = new TextView(getActivity());
params.setMargins(margin, margin, margin, margin);
v.setLayoutParams(params);
v.setLayoutParams(params);
v.setGravity(Gravity.CENTER);
v.setText("聊天界面");
v.setTextSize((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12, dm));
fl.addView(v);
return fl;
}
}

TabMainActivity(定义个Tab,然后设定个ViewPager就行了。Fragment在适配器中设置)

package com.kale.actionbar05.tab;

import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBarActivity;
import android.util.DisplayMetrics;
import android.util.TypedValue; import com.astuetz.PagerSlidingTabStrip;
import com.kale.actionbar05.R; public class TabMainActivity extends ActionBarActivity {
private ChatFragment chatFragment;
private FoundFragment foundFragment;
private ContactsFragment contactsFragment; /**
* PagerSlidingTabStrip的实例
*/
private PagerSlidingTabStrip tabs;
private ViewPager viewPager; @Override
protected void onCreate(Bundle savedInstanceState) {
// TODO 自动生成的方法存根
super.onCreate(savedInstanceState);
setContentView(R.layout.tab_main);
initView(); //initTabs(); //用代码来设置tab的样式,因为我在xml中已经设置了,所以就不在代码中写了
viewPager.setAdapter(new MyPagerAdapter(getSupportFragmentManager()));
tabs.setViewPager(viewPager); } /**
* @author:Jack Tony
* @tips :定义适配器,返回一个fragment对象
* @date :2014-7-30
*/
public class MyPagerAdapter extends FragmentPagerAdapter { public MyPagerAdapter(FragmentManager fm) {
super(fm);
} private final String[] titles = { "聊天", "发现", "通讯录" }; @Override
public CharSequence getPageTitle(int position) {
return titles[position];
} @Override
public int getCount() {
return titles.length;
} @Override
public Fragment getItem(int position) {
switch (position) {
case 0:
if (chatFragment == null) {
chatFragment = new ChatFragment();
}
return chatFragment;
case 1:
if (foundFragment == null) {
foundFragment = new FoundFragment();
}
return foundFragment;
case 2:
if (contactsFragment == null) {
contactsFragment = new ContactsFragment();
}
return contactsFragment;
default:
return null;
}
} } /**
*定义的属性,参考:https://github.com/astuetz/PagerSlidingTabStrip
*/
private void initTabs() {
/**
* 获取当前屏幕的密度
*/
DisplayMetrics dm;
dm = getResources().getDisplayMetrics();
// 设置Tab是自动填充满屏幕,也就是均分屏幕宽度,每个tab的weight都是一致的 app:pstsShouldExpand="true"
tabs.setShouldExpand(true);
// 设置Tab的分割线是透明的 app:pstsDividerColor="@android:color/transparent"
tabs.setDividerColor(Color.TRANSPARENT);
// 设置Tab底部与下面分割的细线的高度 app:pstsUnderlineHeight="1dp"
tabs.setUnderlineHeight((int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 1, dm));
// 设置Tab 指示器的高度 app:pstsIndicatorHeight="4dp"
tabs.setIndicatorHeight((int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 4, dm));
// 设置Tab标题文字的大小 app:pstsTabTextSize="16sp"
tabs.setTextSize((int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 16, dm));
// 设置Tab Indicator的颜色 app:pstsIndicatorColor="#45c01a"
tabs.setIndicatorColor(Color.parseColor("#45c01a"));
// 设置选中Tab文字的颜色 app:pstsSelectedTabTextColor="#45c01a"
tabs.setSelectedTextColor(Color.parseColor("#45c01a"));
// 取消点击Tab时的背景色 app:pstsTabBackground="0"
tabs.setTabBackground(0);
} private void initView() {
tabs = (PagerSlidingTabStrip) findViewById(R.id.tabs);
viewPager = (ViewPager) findViewById(R.id.viewPager03);
}
}

源码下载:http://download.csdn.net/detail/shark0017/7696295

题外话:通过这几篇文章的学习,我可以肯定的说:我们已经不需要ActionBarSherlock(最高版本4.4.0)这个开源库了。其实这是件好事,很感谢国外的这个团体开发的这么有效稳定的库,给我们开发者带来了极大的帮助。离开这个未免有些伤感,我还记得之前看到它的Demo时那种激动,整天学习它代码的那种苦,还有找到解决办法的兴奋。但无论怎样,那都是历史了。我们需要迎接新的科技,迎接更好的明天!

最后,向ActionBarSherlock-4.4.0致敬!

低版本系统兼容的ActionBar(六)用Fragment+ViewPager+Tab实现快速导航的更多相关文章

  1. 低版本系统兼容的ActionBar(四)添加Tab+添加自定义的Tab视图+Fragment

    在ActionBar中添加Tab是很有用的技巧.在support V7库的支持下,我们几乎可以用和之前一样的方式来添加Tab,对于Tab来说,我们可以和MenuItem一样,给他定义自己的视图.我这里 ...

  2. 低版本系统兼容的ActionBar(二)ActionProvider+分离式ActionBar+分离式的ActionMode

           这篇文章主要讲的是在低版本兼容的ActionBar中实现自定义的ActionProvider,ShareActionProvider的使用方法,如何实现分离式ActionBar,外加在分 ...

  3. 低版本系统兼容的ActionBar(一)设置颜色+添加Menu+添加ActionMode

        之前我一直用ActionBarSherlock这个开源项目来做ActionBar,因为它可以让低版本的设备也能用上ActionBar.但是在最新的SDK中Google提供了一个AppCompa ...

  4. 低版本系统兼容的ActionBar(七)自定义Actionbar标题栏字体

    这个自定义字体其实和ActionBar有关,但之前写AtionBar的时候没考虑到修改字体样式,今天看到一篇专门写这个的文章就贴上使用方式.╮(╯▽╰)╭,不得不说Actionbar的那个样式真是让人 ...

  5. 低版本系统兼容的ActionBar(五)修改ActionBar的全套样式,从未如此简单过

         设定ActionBar的样式,是我们必须掌握的技能,在之前我们可能都需要一行一行的写代码,然后在模拟器上测试效果,但是现在我们有个一个很棒的工具来设定样式.设定ActionBar样式的工作从 ...

  6. 低版本系统兼容的ActionBar(三)自定义Item视图+进度条的实现+下拉导航+透明ActionBar

           一.自定义MenuItem的视图 custom_view.xml (就是一个单选按钮) <?xml version="1.0" encoding="u ...

  7. hyperscan在低版本系统应用问题

    编译环境:centos6.3 32位/64位 由于hyperscan使用许多C++11特性,在低版本系统gcc版本不能编译.后来发现在runtime时也就是hs_scan时只需要依赖libhs_run ...

  8. Android 高版本API方法在低版本系统上的兼容性处理

    Android 版本更替,新的版本带来新的特性,新的方法. 新的方法带来许多便利,但无法在低版本系统上运行,如果兼容性处理不恰当,APP在低版本系统上,运行时将会crash. 本文以一个具体的例子说明 ...

  9. angularjs1+requirejs+ bootstrap+ jQuery低版本配合兼容ie8+浏览器

    angularjs兼容低版本IE浏览器(IE8)angularjs在1.3之后的版本都是选择放弃对IE8及更低IE版本的支持,但是就目前的开发形式来看,IE8的使用客户还是蛮多的,最近有个项目要求尽量 ...

随机推荐

  1. ASP.Net1

    一.Web应用程序与传统桌面应用程序的不同: 1.产品级的Web应用程序总是包括至少两台联网的机器:一台承载网站,另一台在Web浏览器中查看数据. 即:我们通过自己的电脑浏览Web程序,这个程序会向服 ...

  2. [转] HTML 获取屏幕、浏览器、页面的高度宽度

    本篇主要介绍Web环境中屏幕.浏览器及页面的高度.宽度信息. 目录 1. 介绍:介绍页面的容器(屏幕.浏览器及页面).物理尺寸与分辨率.展示等内容. 2. 屏幕信息:介绍屏幕尺寸信息:如:屏幕.软件可 ...

  3. 【AtCoder】Dwango Programming Contest V题解

    A - Thumbnail 题意简述:给出N个数,找出N个数中和这N个数平均值绝对值最小的数 根据题意写代码即可= = #include <bits/stdc++.h> #define f ...

  4. Java反射初探 ——“当类也学会照镜子”

    反射的作用 开门见山地说说反射的作用   1.为我们提供了全面的分析类信息的能力 2.动态加载类   我理解的“反射”的意义 (仅个人理解哈)   我理解的java反射机制就是: 提供一套完善而强大的 ...

  5. linux学习笔记-9.查找

    1.查找可执行的命令 which ls 2.查找可执行的命令和帮助的位置 whereis ls 3.查找文件(需要更新库:updatedb) locate hadoop.txt 4.从某个文件夹开始查 ...

  6. Redis五大类型操作使用以及订阅发布功能

    redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sorted set ...

  7. 【BZOJ 1061】 1061: [Noi2008]志愿者招募 (线性规划与网络流)**

    1061: [Noi2008]志愿者招募 Description 申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管.布布刚上任就遇到了一个难 题:为即将启动的奥运新项目招募一批短 ...

  8. 清北学堂省选刷题冲刺班 Test Day3

    目录 2018.3.27 Test T1 T2 T3 考试代码 T2 T3 2018.3.27 Test 时间:8:00~11:30 期望得分:100+60+25=185 实际得分:100+40+25 ...

  9. spring cloud 学习(4) - hystrix 服务熔断处理

    hystrix 是一个专用于服务熔断处理的开源项目,当依赖的服务方出现故障不可用时,hystrix有一个所谓的断路器,一但打开,就会直接拦截掉对故障服务的调用,从而防止故障进一步扩大(类似中电路中的跳 ...

  10. css卷叶效果

    <!DOCTYPE HTML><html lang="en-US"><head> <meta charset="UTF-8&qu ...